Migrate all your media (photos and videos) from GoPro Premium to Google Photos so you can cancel your GoPro Premium / Plus subscription without losing your files.
The script works in a stream-based, disk-efficient way: one file is downloaded, uploaded, and then deleted from disk before the next file is processed. Only one file occupies your local disk at any time.
- What this project does
- Prerequisites
- Finding your GoPro auth token and user ID
- Setting up Google Cloud OAuth credentials
- Installation and usage
- Configuration options
- Troubleshooting / FAQ
| Feature | Details |
|---|---|
| Minimal disk usage | Downloads one file at a time, uploads it, then deletes the local copy |
| Resume support | Stores progress in migration_state.json; restart safely after interruptions |
| Retry / backoff | Automatically retries on network errors and API rate limits |
| Resumable uploads | Uses Google's resumable upload protocol for large video files |
| EXIF timestamps | Preserves the original capture date on JPEG photos so Google Photos sorts them correctly |
| Smart ordering | Processes smallest files first so you get fast initial progress |
| Progress bar | Live terminal progress via tqdm |
| Logging | Human-readable log written to migration.log |
- Python 3.10 or newer
- An active GoPro Premium / Plus subscription with media stored in GoPro Premium cloud
- A Google account with enough free storage for your media (Google Photos counts towards your 15 GB free Google storage quota)
GoPro does not offer a public OAuth flow for third-party scripts, so you need to copy the authentication cookies that your browser already has after you log in to GoPro Premium cloud.
- Open https://gopro.com in your browser and log in.
- Press F12 (or Cmd+Opt+I on macOS) to open DevTools.
- Click the Application tab (you may need to click
»to find it). - In the left sidebar, expand Cookies → click
https://gopro.com. - Find the cookie named
gp_access_token– copy its Value. - Find the cookie named
gp_user_id– copy its Value.
Note: These tokens expire after some time. If the script starts returning 401 errors, repeat the steps above to get fresh tokens.
- Log in to https://gopro.com.
- Press F12 → Storage tab → expand Cookies → click
https://gopro.com. - Copy the values of
gp_access_tokenandgp_user_id.
- Go to https://console.cloud.google.com/.
- Click Select a project → New Project.
- Give it a name (e.g.
gopro-migration) and click Create.
- In the left menu go to APIs & Services → Library.
- Search for Photos Library API and click Enable.
Note: Google has redesigned this UI. If you don't see "OAuth consent screen" under "APIs & Services", look for Google Auth Platform → Branding in the left menu instead.
- Go to APIs & Services → OAuth consent screen (or Google Auth Platform → Branding).
- Select External and click Create.
- Fill in the required fields (App name, user support email, developer email).
- Click Save and Continue through the remaining steps.
- On the Test users (or Audience) screen, add your own Google account as a test user.
- Go to APIs & Services → Credentials (or Google Auth Platform → Clients).
- Click Create Credentials → OAuth client ID (or Create Client).
- Choose Desktop app as the application type.
- Give it a name and click Create.
- Click Download JSON and save the file as
client_secrets.jsonin the project directory.
# 1. Clone or download this project
git clone https://github.com/mlauenborg/gopro-to-google-migration.git
cd gopro-to-google-migration
# 2. (Recommended) Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Configure credentials
cp .env.example .env
# Edit .env and fill in GOPRO_ACCESS_TOKEN, GOPRO_USER_ID,
# and adjust GOOGLE_CLIENT_SECRETS_FILE if needed.
# 5. Place client_secrets.json in the project directory
# (downloaded from Google Cloud Console – see step 4.4 above)
# 6. Run the migration
python migrate.pyOn the first run a browser window will open asking you to authorise the
script to upload to your Google Photos library. After you approve it, the
token is saved to google_token.json so subsequent runs are non-interactive.
All options can be set in the .env file (copy from .env.example):
| Variable | Default | Description |
|---|---|---|
GOPRO_ACCESS_TOKEN |
(required) | JWT from browser cookie gp_access_token |
GOPRO_USER_ID |
(required) | From browser cookie gp_user_id |
GOOGLE_CLIENT_SECRETS_FILE |
client_secrets.json |
Path to Google OAuth credentials |
GOOGLE_TOKEN_FILE |
google_token.json |
Where the OAuth token cache is stored |
TEMP_DOWNLOAD_DIR |
/tmp/gopro_migration |
Temporary directory for downloads |
MIGRATION_STATE_FILE |
migration_state.json |
Progress tracker (auto-created) |
CHUNK_SIZE |
1048576 (1 MB) |
Download/upload chunk size in bytes |
MAX_RETRIES |
5 |
Maximum retry attempts on transient errors |
RETRY_BACKOFF_BASE |
1.0 |
Initial backoff delay in seconds (doubles each retry) |
GOPRO_PAGE_SIZE |
100 |
Number of items fetched per GoPro API page |
Make sure you have copied .env.example to .env and filled in both
GOPRO_ACCESS_TOKEN and GOPRO_USER_ID.
Your gp_access_token has expired. Log in to gopro.com
again, copy the fresh cookie value, and update your .env file.
Increase MAX_RETRIES in .env and make sure you have a stable internet
connection. The resumable upload will resume where it left off.
The script automatically skips files listed in migration_state.json. You
can safely re-run it at any time; only files that haven't been migrated yet
(or that previously failed) will be processed.
Open migration_state.json and remove the entries under "failed" that you
want to retry, then run the script again.
Because this is a personal project using the "External" OAuth consent type, Google shows a warning screen. Click Advanced → Go to <app name> (unsafe) to proceed. This is expected for personal scripts.
The free tier allows 10 000 requests per day. Each file requires 2 API calls (upload + create), so you can migrate ~5 000 files per day. If you hit the limit, wait 24 hours and re-run the script (it will resume from where it stopped).
Check migration.log in the project directory for a detailed log of every
file, including errors and retry attempts.
Google Photos de-duplicates by content. If your GoPro Premium library contains files that are byte-identical (e.g. the same clip saved twice), Google Photos will only store one copy. The migration still counts each upload as successful — this is normal.
After verifying your files are in Google Photos:
- Revoke Google API access — go to myaccount.google.com/permissions and remove the app.
- Delete local credentials — remove
client_secrets.json,google_token.json, and.envfrom the project directory. - Optionally delete the Google Cloud project at console.cloud.google.com.
gopro-to-google-migration/
├── migrate.py # Entry point – run this
├── gopro_client.py # GoPro Premium API client
├── google_photos_client.py # Google Photos API client
├── migration_engine.py # Core loop: download → upload → delete → log
├── config.py # Configuration loader
├── requirements.txt # Python dependencies
├── .env.example # Template for .env credentials file
├── .gitignore
└── README.md
This project is released under the MIT License.