iTranslated by AI
Kaggle Competitions: A CI/CD Workflow with GitHub Actions and the Kaggle API
Introduction
Are you editing Kaggle Notebooks directly in the browser editor?
The browser editor has several inconveniences, such as the inability to use Git for version control, difficulty in checking diffs, and the inability to use your preferred editor (like VSCode).
In this article, I will introduce a workflow that consolidates Notebook code management into GitHub and automatically deploys to Kaggle simply by running git push. You can leverage the entire GitHub ecosystem (Actions, Secrets, version control) for Kaggle competitions.
This workflow is based on what I actually used for the Deep Past Challenge (Akkadian-to-English translation competition).
The Final Workflow
Edit Notebook → git push → GitHub Actions → Upload to Kaggle → Submit in Browser
- Edit
.ipynblocally or on any device git add && git commit && git pushgh workflow run kaggle-push.yml -f notebook_dir=deep-past- GitHub Actions uploads the notebook using the Kaggle API
- Open the Kaggle Notebook screen in your browser and click "Submit to Competition"
Why Can't It Be Fully Automated?
Initially, I planned to automate the entire process through submission, but due to Kaggle API limitations, Notebook submission for code competitions (CreateCodeSubmission) cannot be executed via API (as of February 2026).
requests.exceptions.HTTPError: 403 Client Error: Forbidden
Error details: Permission 'kernelSessions.get' was denied
Technical Reasons
-
Restriction on permission scopes:
kernelSessions.getis a permission for monitoring the execution session state of a notebook, and public API tokens (both legacy and new formats) do not include this scope. - Uniqueness of code-submission competitions: Unlike simple CSV file uploads, notebook submission involves re-execution and progress monitoring on the platform, requiring higher control privileges.
-
File submission (
competitions submit) is also not allowed: In code competitions, direct CSV uploads are rejected (400 Bad Request).
Verified Combinations
| Auth Method | CLI Version | API | Result |
|---|---|---|---|
| Legacy API Key | 1.8.4 | competition_submit_code |
403 |
| New API Token (KGAT_...) | 1.8.4 | competition_submit_code |
403 |
| New API Token (KGAT_...) | 2.0.0 | competition_submit_code |
403 |
| New API Token (KGAT_...) | 2.0.0 |
competitions submit (file) |
400 |
In conclusion, until the API specification is updated, a hybrid operation where you "automate up to code deployment and perform the final submission in the browser" is the most realistic approach.
Directory Structure
kaggle-competitions/
├── .github/workflows/
│ └── kaggle-push.yml # Automation for pushing to Kaggle
├── deep-past/ # Create directories per competition
│ ├── kernel-metadata.json
│ └── deep-past-baseline.ipynb
└── KAGGLE_WORKFLOW.md
kernel-metadata.json
{
"id": "your-username/your-kernel-slug",
"title": "Your Kernel Title",
"code_file": "your-notebook.ipynb",
"language": "python",
"kernel_type": "notebook",
"is_private": "true",
"enable_gpu": "false",
"enable_internet": "false",
"dataset_sources": [],
"competition_sources": ["competition-slug"],
"kernel_sources": [],
"model_sources": []
}
Important Points
-
enable_internet: "false": Required for code competitions. If Internet is ON, the notebook will not be eligible for submission. -
competition_sources: Mounts the competition data. Note the path carefully (explained later). -
is_private: "true": For submission. Create a separate notebook for public versions aimed at medals.
GitHub Actions Workflow
name: Kaggle Kernels Push
on:
workflow_dispatch:
inputs:
notebook_dir:
description: 'Notebook directory (e.g., deep-past)'
required: true
type: string
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install kaggle CLI
run: pip install kaggle
- name: Push notebook to Kaggle
env:
KAGGLE_API_TOKEN: ${{ secrets.KAGGLE_API_TOKEN }}
run: kaggle kernels push -p "${{ inputs.notebook_dir }}"
Setting up GitHub Secrets
| Secret | Content |
|---|---|
KAGGLE_API_TOKEN |
Obtained via Kaggle Settings → API Tokens → Generate (KGAT_... format) |
Common Pitfalls
1. The Data Path Trap
The mount point for competition_sources is:
/kaggle/input/competitions/<competition-slug>/
It is NOT /kaggle/input/<competition-slug>/. The competitions/ subdirectory must be included.
Debugging code:
from pathlib import Path
for item in sorted(Path('/kaggle/input').iterdir()):
print(f' {item.name}/')
for sub in sorted(item.iterdir()):
print(f' {sub.name} ({sub.stat().st_size:,} bytes)')
2. Windows Encoding Problem with kernels output
Logs containing non-ASCII characters (like Akkadian) cause cp932 errors with kaggle kernels output. Avoid this by hitting the API directly:
import base64, json, urllib.request, pathlib
creds = json.loads(pathlib.Path.home().joinpath('.kaggle/kaggle.json').read_text())
username = creds['username']
key = creds['key']
auth = base64.b64encode(f'{username}:{key}'.encode()).decode()
url = f'https://www.kaggle.com/api/v1/kernels/output?userName={username}&kernelSlug=your-kernel-slug'
req = urllib.request.Request(url)
req.add_header('Authorization', f'Basic {auth}')
resp = urllib.request.urlopen(req)
outer = json.loads(resp.read().decode('utf-8'))
log = json.loads(outer['logNullable'])
for e in log:
if e.get('stream_name') == 'stdout':
print(e['data'], end='')
3. Cannot submit if Internet is ON
If you set enable_internet: "true" in a code competition, the notebook will execute normally but will not be recognized as a valid submission. Make sure to set it to "false".
Operational Flow (Example)
The actual flow for the Deep Past Challenge (Akkadian-to-English translation competition):
- Create a baseline Notebook locally (TF-IDF nearest neighbor)
-
git push→ GitHub Actions → Upload to Kaggle - Submit in browser
- Baseline submission completed with Public Score: 5.6 (1795th place)
You can perform iterations to improve your score using the same flow.
Summary
| Process | Automation | Method |
|---|---|---|
| Notebook editing | - | Local / Any device |
| Upload to Kaggle | Automated | GitHub Actions + kaggle kernels push
|
| Submission | Manual | "Submit to Competition" in browser |
| Score check | Automated |
kaggle competitions submissions API |
While full automation could not be achieved, because code management and deployment are handled via GitHub, the code is reflected on Kaggle just by running git push from any device.
Only the single click to press the submit button remains manual, but all other processes are completed in the cloud.
Discussion