Skip to content

API Overview

Use the Framedash REST API to retrieve and analyze telemetry data.

ServiceURL
Web API (Projects, Maps, Content, Query, Analytics, Alerts, Data)https://app.framedash.dev/api
Ingest API (Event Ingestion)https://ingest.framedash.dev

The REST API under /api/v1 accepts either an API key or an OAuth 2.1 Bearer token. If a request sends both, the X-API-Key header takes precedence. Most integrations use an API key, sent via the X-API-Key header:

X-API-Key: fd_your_api_key_here

Create API keys from the project’s “API Keys” page in the dashboard. New keys use the neutral fd_ prefix; authorization comes from the key’s stored scopes, not from the prefix.

On the Free plan, a project can have at most 2 active API keys, and each key value is shown only once at creation. If you hit the cap, deactivate an unused key before creating a new one.

Endpoints that are not scoped to a project in the URL path (such as the Content Registry endpoints under /v1/content) also require an X-Project-Id header naming the target project:

X-Project-Id: your-project-uuid
PresetScopesUsage
Ingestevents:writeSDK event ingestion only
Read-onlyanalytics:readDashboard/status/analytics reads, CLI reads, and MCP tools except raw SQL
Read & Writeanalytics:read, resources:writeRead access plus map, content, and alert resource changes
Fullanalytics:read, resources:write, data:adminRead/write access plus raw SQL query, data export, and player erasure

GET /v1/whoami validates any active API key and returns non-sensitive metadata about it, without requiring a particular scope. This lets you confirm that a key works even when it cannot reach any read endpoint, such as an Ingest key that only carries events:write. Only the X-API-Key header is accepted here; an OAuth Bearer token is not.

Terminal window
curl https://app.framedash.dev/api/v1/whoami \
-H "X-API-Key: fd_your_api_key_here"

The response is tiered by scope. A key with a read or management scope (analytics:read, resources:write, or data:admin) returns full metadata { projectId, projectName, tenantId, planId, scopes }; a data-plane-only key returns just { projectId, scopes }. The raw key value is never returned, and a missing or invalid key returns 401.

This endpoint has its own rate limit of 60 requests per hour per tenant, separate from your plan quota; exceeding it returns 429 with a Retry-After header.

The REST API also accepts an OAuth 2.1 Bearer token in place of an API key:

Authorization: Bearer <access-token>

Framedash is an OAuth 2.1 authorization server. Clients discover its endpoints from {origin}/.well-known/oauth-authorization-server, where {origin} is https://app.framedash.dev:

EndpointURL
Authorization{origin}/oauth/authorize
Token{origin}/api/oauth/token
Dynamic client registration (RFC 7591){origin}/api/oauth/register
Revocation{origin}/api/oauth/revoke

Only the authorization code flow with PKCE (S256) is supported, for public clients (a token_endpoint_auth_method of none). The grant types are authorization_code and refresh_token.

An OAuth token can carry only the analytics:read and resources:write scopes; when a client requests no scope, it is granted analytics:read. The data:admin, events:write, and events:synthetic scopes are never granted over OAuth, so operations that need them stay API-key-only. For example, POST /v1/query needs data:admin, so it cannot be called with an OAuth token.

Authorized OAuth apps are listed under Settings -> Connected apps in the dashboard. Each entry shows the client name, granted scopes, consented projects, and its created and last-used times, and can be revoked individually.

Ingested events are queryable with SQL. Send project_id in the JSON body (not a header) and authenticate with X-API-Key. Raw SQL requires the data:admin scope (the Full preset):

Terminal window
curl -X POST https://app.framedash.dev/api/v1/query \
-H "X-API-Key: fd_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"project_id": "your-project-uuid",
"sql": "SELECT event_name, count() AS events FROM events WHERE timestamp >= today() GROUP BY event_name ORDER BY events DESC",
"limit": 100
}'

The response is { "success": true, "data": { "rows": [...], "rowCount": N } }. A rejected query returns HTTP 400 with the ClickHouse diagnostic message (unknown column, table, or function; syntax errors; type mismatches; forbidden operations); only infrastructure failures return 500. See the Events Schema for the columns and the validator rules, and Troubleshooting to confirm events arrived first.

The Ingest endpoint (POST /v1/events) accepts only application/x-protobuf (a JSON body returns 415). Events are produced by the official Unity, UE5, and Godot SDKs, which encode the protobuf TelemetryBatch and set the required headers for you. Hand-posting an event means encoding that protobuf schema yourself and sending X-API-Key (events:write scope), Content-Type: application/x-protobuf, Content-Length, and X-SDK-Version. For nearly all integrations, use an SDK rather than posting directly.

The API rate limit is enforced per account (tenant) and counted as requests per hour. All projects and API keys belonging to the same account share one limit. The Free plan allows 100 requests per hour; paid plans allow more.

Every response carries these three headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1782968400000

X-RateLimit-Reset is a Unix timestamp in milliseconds, not seconds.

Exceeding the limit returns 429. On that response, X-RateLimit-Remaining is 0 and a Retry-After header (the number of seconds to wait) is added:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1782968400000
Retry-After: 3400

The limit is computed with a sliding window. As a result, requests can still be limited right after the time X-RateLimit-Reset advertises, and Retry-After can jump to nearly a full hour. Honor the Retry-After on the 429 response rather than scheduling a retry for the reset time. On normal responses, read X-RateLimit-Remaining and space out requests before you hit the limit.

The Web API (app.framedash.dev/api) and the Ingest API (ingest.framedash.dev) return different error shapes. Do not assume a single parser handles both; handle each endpoint’s shape separately.

On success:

{
"success": true,
"data": { ... }
}

On error, the API returns RFC 9457 Problem Details with the application/problem+json content type:

{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "Plan limit reached.",
"error_category": "authorization",
"retryable": false
}
MemberDescription
typeProblem type URI; about:blank when no specific type applies.
titleShort, human-readable summary of the HTTP status.
statusHTTP status code.
detailHuman-readable explanation of this specific error.
error_categoryOne of authentication, authorization, validation, rate_limit, not_found, conflict, payload, internal.
retryableWhether retrying may succeed (true for 429 and 503).
retry_afterOptional. Suggested retry delay in seconds.

On success:

{
"status": "accepted"
}

On error:

{
"error": "Error message"
}

The API specification is available for download in OpenAPI 3.1 format.

See the auto-generated API reference in the sidebar for full details.

EndpointMethodDescription
/v1/projectsGETList the project bound to the API key
/v1/projects/{id}/statusGETGet project status
EndpointMethodDescription
/v1/whoamiGETValidate an API key and return non-sensitive key metadata
EndpointMethodDescription
/v1/projects/{id}/dashboardGETGet dashboard metrics (DAU, MAU, sessions, events)
/v1/projects/{id}/buildsGETList build IDs with performance data
/v1/projects/{id}/builds/compareGETCompare performance between two builds
/v1/projects/{id}/heatmapGETGet heatmap data for a map
/v1/projects/{id}/retentionGETGet player retention cohorts
/v1/projects/{id}/funnelsGETGet funnel conversion analysis
/v1/projects/{id}/insightsGETGet aggregated analytics by dimension
EndpointMethodDescription
/v1/projects/{id}/alertsGETList alert rules
/v1/projects/{id}/alertsPOSTCreate an alert rule
/v1/projects/{id}/alerts/{alertId}GETGet an alert rule
/v1/projects/{id}/alerts/{alertId}PATCHUpdate an alert rule
/v1/projects/{id}/alerts/{alertId}DELETEDeactivate an alert rule
/v1/projects/{id}/alerts/historyGETList alert evaluation history
/v1/projects/{id}/threshold-profilesGETList threshold profiles
EndpointMethodDescription
/v1/projects/{id}/mapsGETList maps
/v1/projects/{id}/maps/{mapId}DELETEDelete a map
/v1/maps/uploadPOSTUpload a map
EndpointMethodDescription
/v1/contentGETList content entries
/v1/contentPOSTCreate or update content entries
/v1/contentDELETEDelete a content entry
EndpointMethodDescription
/v1/queryPOSTExecute an analytics query
EndpointMethodDescription
/v1/data/exportGETExport accessible project data
/v1/data/erasePOSTErase telemetry for one player
EndpointMethodDescription
/v1/eventsPOSTIngest telemetry events