API Reference
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.
| Method | Location | Parameter | Description |
|---|---|---|---|
| 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. |
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 Session cookie set after browser login.
System
Health checks, system status, and API metadata.
/health
Health CheckPublic 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"
}/status
System StatusSingle-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
}
}/openapi.json
OpenAPI SpecificationReturns 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
/jobs/{id}
Get Job StatusReturns 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,
resultincludes the latest run history - If the job is still queued or running,
resultis null
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | yes | Job queue ID (returned by endpoints that create jobs) |
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
/backups
List BackupsRequest
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"
}
]/backups
Create BackupAsync. 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
}/backups/{filename}
Download BackupDownload 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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| filename | path | string | yes | Backup filename (must match `backup-*.tar.gz`) |
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"/backups/{filename}
Delete BackupParameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| filename | path | string | yes | Backup filename |
Backup filename
Request
curl -X DELETE "${PROFILARR_URL}/api/v1/backups/{filename}" \
-H "X-Api-Key: ${API_KEY}"Responses
{
"success": true
}/backups/upload
Upload BackupOnly .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
{
"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"
}/backups/settings
Get Backup SettingsRequest
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
}/backups/settings
Update Backup SettingsPartial update. After saving, backup jobs are rescheduled to match the new settings.
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
/arr
List Arr InstancesReturns 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_keyfield 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"
}
]Databases
Linked database instance management
/databases
List DatabasesSecrets 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
}
]/databases
Link DatabaseClones the repository, validates its PCD manifest, and processes dependencies. This is synchronous and may take several seconds for large repositories.
Field dependencies:
personal_access_tokenrequiresgit_user_nameandgit_user_emaillocal_ops_enabledrequirespersonal_access_tokenconflict_strategyis only accepted without a PAT, or with PAT +local_ops_enabled
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
}/databases/{id}
Get DatabaseSecrets are stripped: personal_access_token is replaced by hasPat (boolean)
and local_path is excluded.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | yes | Database instance ID |
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
}/databases/{id}
Update DatabasePartial update. Only the provided fields are changed. Unknown fields are silently ignored.
Field dependencies:
personal_access_tokenrequiresgit_user_nameandgit_user_emaillocal_ops_enabledrequirespersonal_access_tokenconflict_strategyis only accepted without a PAT, or with PAT +local_ops_enabled
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | yes | Database instance ID |
Database instance ID
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
}/databases/{id}
Unlink DatabaseRemoves the database row, deletes the cloned repository from disk, and cancels any scheduled sync jobs.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | yes | Database instance ID |
Database instance ID
Request
curl -X DELETE "${PROFILARR_URL}/api/v1/databases/{id}" \
-H "X-Api-Key: ${API_KEY}"Responses
{
"error": "Unauthorized"
}/databases/{id}/sync
Trigger SyncAsync. Enqueues a pcd.sync job and returns 202 with the job ID.
Poll GET /api/v1/jobs/{jobId} for completion.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | integer | yes | Database instance ID |
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
/announcements
List AnnouncementsVisible 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
}
]/announcements/{id}
Get AnnouncementReturns 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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | yes | ULID of the announcement. |
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
}