This site is still a work in progress. For the current docs, head to dictionarry.dev.
profilarr profilarr /docs

API Reference

v1
Info
API v1 is still in progress and subject to change. It is not complete. Use at your own risk.

Profilarr manages configuration for Radarr and Sonarr by syncing curated configuration databases into Arr instances.

Authentication

Profilarr requires authentication on all API endpoints via either an X-Api-Key header or a session cookie. API keys are for programmatic access and restricted to /api/ paths; session cookies are set after browser login. Authenticated users never see raw secrets. Sensitive values are stripped from all responses and only accessible with filesystem access to the host. The only exception is the health endpoint, which is public and requires no authentication. It is intentionally barebones so uptime monitors can poll it without credentials.

apiKey header
X-Api-Key

API key for programmatic access. Create keys in Settings > Security. Each key has either full access or per-area access. Areas match the tags in this spec, and each operation's `x-permission` says whether it needs `read` or `write` access to its area. Write access includes read. A key without the required access gets 403, and an expired key gets 401.

session cookie
session

Session cookie set after browser login.

System

Health checks, system status, and API metadata.

GET

/health

Health Check

Public endpoint, no authentication required. Use this for uptime monitors and load balancers. Returns 200 for healthy or degraded, 503 for unhealthy.

Request

curl -X GET "${PROFILARR_URL}/api/v1/health" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "status": "healthy",
  "timestamp": "2026-03-09T12:00:00.000Z"
}
GET

/status

System Status

Single-call overview designed for dashboard integrations (e.g. Homepage). The drift field on each arr is reserved for future use and always null.

Request

curl -X GET "${PROFILARR_URL}/api/v1/status" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "version": "2.0.0",
  "uptime": 86400,
  "timezone": "America/New_York",
  "databases": [
    {
      "id": 1,
      "name": "Dictionarry",
      "enabled": true,
      "lastSyncedAt": "2026-04-09T12:00:00Z",
      "syncStrategy": "auto",
      "counts": {
        "customFormats": 52,
        "qualityProfiles": 3,
        "regularExpressions": 120,
        "delayProfiles": 1
      }
    }
  ],
  "arrs": [
    {
      "id": 1,
      "name": "Radarr",
      "type": "radarr",
      "enabled": true,
      "sync": {
        "qualityProfiles": {
          "count": 2,
          "status": "idle",
          "lastSyncedAt": "2026-04-09T12:00:00Z"
        },
        "delayProfiles": {
          "configured": true,
          "status": "idle",
          "lastSyncedAt": "2026-04-09T12:00:00Z"
        },
        "mediaManagement": {
          "configured": true,
          "status": "idle",
          "lastSyncedAt": "2026-04-09T12:00:00Z"
        }
      },
      "drift": null
    }
  ],
  "jobs": {
    "active": 0,
    "queued": 2,
    "nextRunAt": "2026-04-09T13:00:00Z"
  },
  "backups": {
    "enabled": true,
    "lastBackupAt": "2026-04-09T00:00:00Z"
  },
  "announcements": {
    "unread": 2
  }
}
GET

/openapi.json

OpenAPI Specification

Returns the resolved OpenAPI 3.1 spec as a single JSON document. Useful for generating client SDKs or feeding to documentation tools.

Request

curl -X GET "${PROFILARR_URL}/api/v1/openapi.json" \
  -H "X-Api-Key: ${API_KEY}"

Responses

"string"

Jobs

Job queue status and polling

GET

/jobs/{id}

Get Job Status

Returns the current status of a job queue entry.

Use cases:

  • Polling for completion after creating a backup
  • Checking if a scheduled job has run
  • Inspecting job errors after failure

Behavior:

  • Returns the job queue record with its current status
  • If the job has executed, result includes the latest run history
  • If the job is still queued or running, result is null

Parameters

id integer required

Job queue ID (returned by endpoints that create jobs)

Request

curl -X GET "${PROFILARR_URL}/api/v1/jobs/{id}" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "id": 42,
  "jobType": "backup.create",
  "status": "success",
  "source": "manual",
  "createdAt": "2026-03-15T10:00:00.000Z",
  "startedAt": "2026-03-15T10:00:01.000Z",
  "finishedAt": "2026-03-15T10:00:05.000Z",
  "result": {
    "status": "success",
    "output": "Backup created: backup-2026-03-15-100005.tar.gz (12.34 MB)",
    "error": null,
    "durationMs": 4000
  }
}

Backups

Backup creation, listing, download, upload, and settings

GET

/backups

List Backups

Request

curl -X GET "${PROFILARR_URL}/api/v1/backups" \
  -H "X-Api-Key: ${API_KEY}"

Responses

[
  {
    "filename": "backup-2026-03-15-100005.tar.gz",
    "created": "2026-01-01T00:00:00Z",
    "size": 0,
    "sizeFormatted": "12.34 MB"
  }
]
POST

/backups

Create Backup

Async. Enqueues a backup.create job and returns 202 with the job ID. Poll GET /api/v1/jobs/{jobId} for completion. The backup includes a sanitized database copy with all secrets stripped.

Request

curl -X POST "${PROFILARR_URL}/api/v1/backups" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "jobId": 42
}
GET

/backups/{filename}

Download Backup

Download a backup archive. The local file on disk is full-fidelity, but the downloaded copy is sanitized on the fly so it is safer to share. The following are removed from the downloaded archive:

  • Arr instances (URLs, API keys, sync configs, drift state, rename and cleanup history)
  • Notification services (webhook URLs, tokens, history)
  • User accounts and active sessions
  • Personal access tokens for linked databases
  • AI and TMDB API keys

The local archive on the server is not modified. Restoring the downloaded file on a different host will require re-adding the removed items.

Parameters

filename string required

Backup filename (must match `backup-*.tar.gz`)

Request

curl -X GET "${PROFILARR_URL}/api/v1/backups/{filename}" \
  -H "X-Api-Key: ${API_KEY}"

Responses

"string"
DELETE

/backups/{filename}

Delete Backup

Parameters

filename string required

Backup filename

Request

curl -X DELETE "${PROFILARR_URL}/api/v1/backups/{filename}" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "success": true
}
POST

/backups/upload

Upload Backup

Only .tar.gz files, max 1GB. Archive contents are scanned for path traversal entries (zip slip protection). Files without a backup- prefix are renamed to backup-uploaded-{timestamp}.tar.gz. Duplicates rejected.

Request Body

Request Body
{
  "file": "string"
}

Request

curl -X POST "${PROFILARR_URL}/api/v1/backups/upload" \
  -H "X-Api-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
  "file": "string"
}'

Responses

{
  "filename": "backup-uploaded-1710504000000.tar.gz",
  "size": 12345678,
  "sizeFormatted": "11.77 MB"
}
GET

/backups/settings

Get Backup Settings

Request

curl -X GET "${PROFILARR_URL}/api/v1/backups/settings" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "schedule": "daily",
  "retentionDays": 30,
  "enabled": true,
  "includeDatabase": true,
  "compressionEnabled": true
}
PATCH

/backups/settings

Update Backup Settings

Partial update. After saving, backup jobs are rescheduled to match the new settings.

Request Body

Request Body
{
  "schedule": "hourly",
  "retentionDays": 0,
  "enabled": false
}

Request

curl -X PATCH "${PROFILARR_URL}/api/v1/backups/settings" \
  -H "X-Api-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
  "schedule": "hourly",
  "retentionDays": 0,
  "enabled": false
}'

Responses

{
  "schedule": "hourly",
  "retentionDays": 0,
  "enabled": false,
  "includeDatabase": false,
  "compressionEnabled": false
}

Arr

Arr instance management, library, and sync endpoints

GET

/arr

List Arr Instances

Returns all Arr instances (Radarr/Sonarr) with secrets stripped.

Use cases:

  • Dashboard widgets showing connected instances
  • Automation scripts checking instance state
  • Prerequisite checks (e.g. onboarding)

Behavior:

  • Returns an empty array if no instances are connected
  • The api_key field is never included

Request

curl -X GET "${PROFILARR_URL}/api/v1/arr" \
  -H "X-Api-Key: ${API_KEY}"

Responses

[
  {
    "id": 0,
    "name": "string",
    "type": "radarr",
    "url": "string",
    "external_url": null,
    "tags": null,
    "enabled": 0,
    "library_refresh_interval": 0,
    "library_last_refreshed_at": null,
    "created_at": "string",
    "updated_at": "string"
  }
]
401 Not authenticated

Databases

Linked database instance management

GET

/databases

List Databases

Secrets are stripped: personal_access_token is replaced by hasPat (boolean) and local_path is excluded.

Request

curl -X GET "${PROFILARR_URL}/api/v1/databases" \
  -H "X-Api-Key: ${API_KEY}"

Responses

[
  {
    "id": 0,
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "name": "string",
    "repository_url": "string",
    "sync_strategy": 0,
    "auto_pull": 0,
    "enabled": 0,
    "is_private": 0,
    "local_ops_enabled": 0,
    "git_user_name": null,
    "git_user_email": null,
    "conflict_strategy": "override",
    "last_synced_at": null,
    "created_at": "string",
    "updated_at": "string",
    "hasPat": false
  }
]
POST

/databases

Link Database

Clones the repository, validates its PCD manifest, and processes dependencies. This is synchronous and may take several seconds for large repositories.

Field dependencies:

  • personal_access_token requires git_user_name and git_user_email
  • local_ops_enabled requires personal_access_token
  • conflict_strategy is only accepted without a PAT, or with PAT + local_ops_enabled

Request Body

Request Body
{
  "name": "string",
  "repository_url": "string",
  "branch": "string",
  "personal_access_token": "string",
  "git_user_name": "string",
  "git_user_email": "string",
  "sync_strategy": 0,
  "auto_pull": false,
  "local_ops_enabled": false,
  "conflict_strategy": "override"
}

Request

curl -X POST "${PROFILARR_URL}/api/v1/databases" \
  -H "X-Api-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "repository_url": "string",
  "branch": "string",
  "personal_access_token": "string",
  "git_user_name": "string",
  "git_user_email": "string",
  "sync_strategy": 0,
  "auto_pull": false,
  "local_ops_enabled": false,
  "conflict_strategy": "override"
}'

Responses

{
  "id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "name": "string",
  "repository_url": "string",
  "sync_strategy": 0,
  "auto_pull": 0,
  "enabled": 0,
  "is_private": 0,
  "local_ops_enabled": 0,
  "git_user_name": null,
  "git_user_email": null,
  "conflict_strategy": "override",
  "last_synced_at": null,
  "created_at": "string",
  "updated_at": "string",
  "hasPat": false
}
GET

/databases/{id}

Get Database

Secrets are stripped: personal_access_token is replaced by hasPat (boolean) and local_path is excluded.

Parameters

id integer required

Database instance ID

Request

curl -X GET "${PROFILARR_URL}/api/v1/databases/{id}" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "name": "string",
  "repository_url": "string",
  "sync_strategy": 0,
  "auto_pull": 0,
  "enabled": 0,
  "is_private": 0,
  "local_ops_enabled": 0,
  "git_user_name": null,
  "git_user_email": null,
  "conflict_strategy": "override",
  "last_synced_at": null,
  "created_at": "string",
  "updated_at": "string",
  "hasPat": false
}
PATCH

/databases/{id}

Update Database

Partial update. Only the provided fields are changed. Unknown fields are silently ignored.

Field dependencies:

  • personal_access_token requires git_user_name and git_user_email
  • local_ops_enabled requires personal_access_token
  • conflict_strategy is only accepted without a PAT, or with PAT + local_ops_enabled

Parameters

id integer required

Database instance ID

Request Body

Request Body
{
  "name": "string",
  "personal_access_token": "string",
  "git_user_name": "string",
  "git_user_email": "string",
  "sync_strategy": 0,
  "auto_pull": false,
  "local_ops_enabled": false,
  "conflict_strategy": "override"
}

Request

curl -X PATCH "${PROFILARR_URL}/api/v1/databases/{id}" \
  -H "X-Api-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "personal_access_token": "string",
  "git_user_name": "string",
  "git_user_email": "string",
  "sync_strategy": 0,
  "auto_pull": false,
  "local_ops_enabled": false,
  "conflict_strategy": "override"
}'

Responses

{
  "id": 0,
  "uuid": "550e8400-e29b-41d4-a716-446655440000",
  "name": "string",
  "repository_url": "string",
  "sync_strategy": 0,
  "auto_pull": 0,
  "enabled": 0,
  "is_private": 0,
  "local_ops_enabled": 0,
  "git_user_name": null,
  "git_user_email": null,
  "conflict_strategy": "override",
  "last_synced_at": null,
  "created_at": "string",
  "updated_at": "string",
  "hasPat": false
}
DELETE

/databases/{id}

Unlink Database

Removes the database row, deletes the cloned repository from disk, and cancels any scheduled sync jobs.

Parameters

id integer required

Database instance ID

Request

curl -X DELETE "${PROFILARR_URL}/api/v1/databases/{id}" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "error": "Unauthorized"
}
204 Database unlinked
POST

/databases/{id}/sync

Trigger Sync

Async. Enqueues a pcd.sync job and returns 202 with the job ID. Poll GET /api/v1/jobs/{jobId} for completion.

Parameters

id integer required

Database instance ID

Request

curl -X POST "${PROFILARR_URL}/api/v1/databases/{id}/sync" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "jobId": 42
}

Announcements

In-app announcements fed from the bulletin repo

GET

/announcements

List Announcements

Visible announcements only: withdrawn, expired, and version-incompatible entries are filtered out server-side. Bodies are not included; use the detail endpoint to fetch one.

Request

curl -X GET "${PROFILARR_URL}/api/v1/announcements" \
  -H "X-Api-Key: ${API_KEY}"

Responses

[
  {
    "id": "01HXYZ0000000000000000000A",
    "title": "API v1 migration landing in 2.4",
    "severity": "warning",
    "publishedAt": "2026-04-10T10:00:00Z",
    "expiresAt": null,
    "minVersion": "2.0.0",
    "maxVersion": null,
    "link": "https://github.com/Dictionarry-Hub/profilarr/discussions/999",
    "readAt": null
  }
]
GET

/announcements/{id}

Get Announcement

Returns the full announcement including its markdown body. The body is lazy-fetched from the bulletin repo on first call and cached locally; subsequent calls read from cache.

404 is returned for unknown ids and for ids that have not yet been ingested by the fetch job (e.g. calling the API between a new bulletin PR merging and the next scheduled reconcile).

Parameters

id string required

ULID of the announcement.

Request

curl -X GET "${PROFILARR_URL}/api/v1/announcements/{id}" \
  -H "X-Api-Key: ${API_KEY}"

Responses

{
  "id": "string",
  "title": "string",
  "severity": "info",
  "publishedAt": "2026-01-01T00:00:00Z",
  "expiresAt": null,
  "minVersion": null,
  "maxVersion": null,
  "link": null,
  "readAt": null,
  "body": null
}