The Problem
The goal: edit a notebook anywhere, push to GitHub, and have it appear on Kaggle ready to submit.
The Workflow
Edit notebook → git push → GitHub Actions → kaggle kernels push → Submit via browser
| Step | Automated? | How |
|---|---|---|
| Edit notebook | - | Any device |
| Upload to Kaggle | Yes | GitHub Actions + kaggle kernels push
|
| Submit | Manual | Browser: "Submit to Competition" |
| Check score | Yes |
kaggle competitions submissions API |
Why Can't We Fully Automate?
I spent a lot of time trying to make submission fully automatic. Here's what I found:
The Kaggle API's CreateCodeSubmission endpoint returns 403 Forbidden:
Permission 'kernelSessions.get' was denied
I tested every combination:
| 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 |
Why?
-
Permission scope limitation:
kernelSessions.getmonitors notebook execution sessions. Public API tokens don't include this scope. - Code competitions are special: Unlike uploading a CSV, notebook submission involves re-execution and progress monitoring on the platform, requiring higher-level control permissions.
- File submission doesn't work either: Code competitions reject direct CSV uploads (400 Bad Request).
Conclusion: Until the API is updated, the hybrid approach (automate deployment, manually submit) is the most practical.
Setup
Directory Structure
kaggle-competitions/
├── .github/workflows/
│ └── kaggle-push.yml
├── deep-past/
│ ├── kernel-metadata.json
│ └── deep-past-baseline.ipynb
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": "false",
"enable_gpu": "false",
"enable_internet": "false",
"competition_sources": ["competition-slug"]
}
Critical: enable_internet must be "false". Internet ON prevents the notebook from being eligible for submission in code competitions.
GitHub Actions Workflow
name: Kaggle Kernels Push
on:
workflow_dispatch:
inputs:
notebook_dir:
description: 'Notebook directory'
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 }}"
Set KAGGLE_API_TOKEN in your repo's GitHub Secrets (Kaggle Settings → API Tokens → Generate).
Gotchas
1. Data Path
competition_sources mounts at:
/kaggle/input/competitions/<slug>/
NOT /kaggle/input/<slug>/. The competitions/ subdirectory is easy to miss.
2. Windows Encoding
kaggle kernels output crashes on Windows with non-ASCII characters (cp932 error). Use the API directly with urllib.request and UTF-8 decoding instead.
3. Kernel Slug Must Match Title
If your kernel-metadata.json title doesn't resolve to the specified id slug, you'll get a 400 error on push. Keep them consistent.
Results
I used this workflow for the Deep Past Challenge (Akkadian → English translation):
- Pushed a TF-IDF nearest neighbor baseline via GitHub Actions
- Submitted via browser
- Public Score: 5.6
The iteration cycle is fast: edit locally → push → submit → check score.
Summary
Full automation of Kaggle code competition submissions isn't possible via the public API (as of Feb 2026). But automating everything up to the submit button still saves a lot of time and lets you work from any device.
The one manual click is a small price to pay.
Top comments (0)