goPodder API v1

May 29, 2026 · View on GitHub

The goPodder API allows you to programmatically manage your podcast synchronization data. Use it to build automation scripts, custom integrations, or third-party tools that interact with your goPodder instance.

Quick Start

# Create an API key in the web UI: Account > API Keys > Create Key
# Then use it to list your gPodder users:
curl -H "Authorization: Bearer gp_your_key_here" https://your-instance/api/v1/users

Authentication

All API v1 endpoints require an API key passed as a Bearer token in the Authorization header:

Authorization: Bearer gp_<your-key>

API keys are created in the web UI under Account > API Keys. The key is shown once at creation and cannot be retrieved later.

Key Roles

RoleAccess
standardManage your own account's gPodder users, devices, and subscriptions
adminEverything above, plus manage all accounts on the instance

Admin keys can only be created by admin accounts. Standard keys are scoped to the account that created them.

Limits

  • Maximum 25 API keys per account
  • Keys are identified by a gp_ prefix
  • Only the bcrypt hash is stored server-side

Errors

All error responses return JSON with an error field:

{"error": "description of what went wrong"}
Status CodeMeaning
400Bad request (invalid input, validation failure)
401Missing or invalid API key
403Key does not have permission for this action
404Resource not found
409Conflict (resource already exists)
500Internal server error

Endpoints

Users

List Users

Returns all gPodder users belonging to your account.

GET /api/v1/users

Required role: standard

Response:

[
  {
    "username": "alice",
    "last_activity": "2026-05-28T14:30:00"
  },
  {
    "username": "bob"
  }
]

The last_activity field is omitted when a user has never synced.

Example:

curl -H "Authorization: Bearer gp_your_key" https://your-instance/api/v1/users

Create User

Creates a new gPodder user linked to your account.

POST /api/v1/users

Required role: standard

Request body:

{
  "username": "alice",
  "password": "a-strong-password"
}
FieldTypeRequiredDescription
usernamestringyes1-64 characters, letters/numbers/dots/dashes/underscores
passwordstringyesMust meet the instance's minimum password length

Response (201):

{"username": "alice"}

Errors:

  • 400 — Invalid username or password too short
  • 403 — User creation is disabled, or user limit reached
  • 409 — Username already exists

Example:

curl -X POST -H "Authorization: Bearer gp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"my-secure-pass"}' \
  https://your-instance/api/v1/users

Delete User

Deletes a gPodder user and all associated data (devices, subscriptions, episode actions).

DELETE /api/v1/users/{username}

Required role: standard

Response: 204 No Content

Errors:

  • 403 — User does not belong to your account
  • 404 — User not found

Example:

curl -X DELETE -H "Authorization: Bearer gp_your_key" \
  https://your-instance/api/v1/users/alice

Devices

List Devices

Returns all devices registered for a user.

GET /api/v1/users/{username}/devices

Required role: standard

Response:

[
  {
    "id": "pixel7",
    "caption": "My Pixel 7",
    "type": "mobile",
    "subscriptions": 42,
    "last_activity": "2026-05-28T14:30:00"
  }
]

The last_activity field shows when the device last synced. It is omitted if the device has never synced.

Example:

curl -H "Authorization: Bearer gp_your_key" \
  https://your-instance/api/v1/users/alice/devices

Subscriptions

List Subscriptions (JSON)

Returns all active podcast feed URLs for a user.

GET /api/v1/users/{username}/subscriptions

Required role: standard

Response:

[
  "https://feeds.example.com/podcast1.xml",
  "https://feeds.example.com/podcast2.xml"
]

Example:

curl -H "Authorization: Bearer gp_your_key" \
  https://your-instance/api/v1/users/alice/subscriptions

Export Subscriptions (OPML)

Returns subscriptions as an OPML document, suitable for importing into other podcast apps.

GET /api/v1/users/{username}/subscriptions.opml

Required role: standard

Response: Content-Type: text/x-opml+xml

Example:

curl -H "Authorization: Bearer gp_your_key" \
  https://your-instance/api/v1/users/alice/subscriptions.opml > subscriptions.opml

Update Subscriptions

Add or remove podcast feed subscriptions.

POST /api/v1/users/{username}/subscriptions

Required role: standard

Request body:

{
  "add": ["https://feeds.example.com/new-podcast.xml"],
  "remove": ["https://feeds.example.com/old-podcast.xml"]
}
FieldTypeRequiredDescription
addstring[]noFeed URLs to subscribe to
removestring[]noFeed URLs to unsubscribe from

Only http:// and https:// URLs are accepted. Invalid URLs are silently filtered. The same URL cannot appear in both add and remove.

Response:

{"timestamp": 1716998400}

The timestamp value can be used with the gPodder sync protocol's since parameter to fetch changes made after this point.

Example:

curl -X POST -H "Authorization: Bearer gp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"add":["https://feeds.example.com/new.xml"],"remove":[]}' \
  https://your-instance/api/v1/users/alice/subscriptions

Accounts (Admin)

These endpoints require an API key with the admin role.

List Accounts

Returns all accounts on the instance.

GET /api/v1/accounts

Required role: admin

Response:

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "admin",
    "role": "admin",
    "created_at": "2026-01-15T10:00:00",
    "last_login": "2026-05-28T09:15:00",
    "last_activity": "2026-05-28T14:30:00"
  },
  {
    "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "username": "team",
    "role": "standard",
    "created_at": "2026-03-01T12:00:00"
  }
]

The last_login field shows when the account last logged into the web UI. The last_activity field shows the most recent sync activity across all gPodder users linked to the account. Both are omitted if never occurred.


Create Account

Creates a new account (web UI login identity).

POST /api/v1/accounts

Required role: admin

Request body:

{
  "username": "newteam",
  "password": "a-strong-password",
  "role": "standard"
}
FieldTypeRequiredDescription
usernamestringyes1-64 characters
passwordstringyesMust meet minimum password length
rolestringnostandard (default) or admin

Response (201):

{"id": "550e8400-...", "role": "standard", "username": "newteam"}

Delete Account

Deletes a non-admin account and all its data (users, devices, subscriptions, episodes, API keys).

DELETE /api/v1/accounts/{id}

Required role: admin

Response: 204 No Content

Errors:

  • 400 — Cannot delete admin accounts via API (use the web UI)
  • 404 — Account not found

List Account Users

Returns all gPodder users belonging to a specific account.

GET /api/v1/accounts/{id}/users

Required role: admin

Response:

[
  {
    "username": "alice",
    "last_activity": "2026-05-28T14:30:00"
  }
]

Usage Examples

Backup all subscriptions to OPML files

#!/bin/bash
API_KEY="gp_your_key_here"
BASE_URL="https://your-instance"

for user in $(curl -s -H "Authorization: Bearer $API_KEY" "$BASE_URL/api/v1/users" | jq -r '.[].username'); do
  curl -s -H "Authorization: Bearer $API_KEY" \
    "$BASE_URL/api/v1/users/$user/subscriptions.opml" > "${user}.opml"
  echo "Exported $user"
done

Sync subscriptions from an OPML file

#!/bin/bash
API_KEY="gp_your_key_here"
BASE_URL="https://your-instance"
USER="alice"

# Extract feed URLs from OPML
FEEDS=$(xmllint --xpath '//outline/@xmlUrl' subscriptions.opml 2>/dev/null | \
  grep -oP 'xmlUrl="\K[^"]+')

# Build JSON payload
ADD=$(echo "$FEEDS" | jq -R . | jq -s .)

curl -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"add\": $ADD, \"remove\": []}" \
  "$BASE_URL/api/v1/users/$USER/subscriptions"

Provision a new user account

#!/bin/bash
API_KEY="gp_your_admin_key"
BASE_URL="https://your-instance"

# Create the web account
ACCT=$(curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username":"newteam","password":"initial-pass-123","role":"standard"}' \
  "$BASE_URL/api/v1/accounts")

echo "Created account: $(echo $ACCT | jq -r .id)"

Rate Limiting

There is currently no rate limiting on the API. Be considerate with request frequency, especially for endpoints that trigger bcrypt password hashing (authentication).

Compatibility

The API v1 (/api/v1/) is separate from the gPodder sync protocol (/api/2/). The sync protocol is used by podcast apps (AntennaPod, gPodder, etc.) for subscription and episode synchronization. The API v1 is for administrative automation and tooling.