Server API
Complete REST API reference for OpenAnalyst. Covers authentication, all resource endpoints, request and response formats, pagination, rate limiting, and error handling.
Base URL and Versioning
All API requests are made over HTTPS to the following base URL:
https://api.openanalyst.com/v1The version segment /v1 is part of every endpoint URL. When a new incompatible API version is released, it will be accessible at /v2 while/v1 remains available for a minimum deprecation period of 12 months. Version sunset dates are announced in the changelog at least 90 days in advance.
Note: The API does not support content negotiation via the Accept-Version header. Always include the explicit version in the URL path.
Authentication
Every request must include a valid API key as a Bearer token in theAuthorization header. API keys are generated from the Settings page inside the web app at app.openanalyst.com.
Authorization: Bearer oa_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxAPI keys are prefixed with oa_live_ for production keys andoa_test_ for test-mode keys. Test-mode keys return synthetic data and do not consume credits or trigger real webhooks.
curl https://api.openanalyst.com/v1/datasets \
-H "Authorization: Bearer oa_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json"Warning: Never expose API keys in client-side JavaScript, mobile app bundles, or public repositories. If a key is compromised, revoke it immediately from the Settings page and issue a replacement.
Request and Response Format
All request bodies must be JSON with the header Content-Type: application/json. All successful responses return JSON with an HTTP 2xx status code. Collections are wrapped in a top-level object with data and pagination keys. Single resources are returned directly as a JSON object.
Success Response — Single Resource
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "rpt_abc123",
"name": "Weekly Executive Summary",
"format": "pdf",
"created_at": "2025-11-01T09:00:00Z",
"updated_at": "2025-11-15T14:22:00Z"
}Success Response — Collection
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{ "id": "rpt_abc123", "name": "Weekly Executive Summary" },
{ "id": "rpt_def456", "name": "Monthly Churn Analysis" }
],
"pagination": {
"cursor": "cur_xyz789",
"has_more": true,
"total": 47
}
}Pagination
All list endpoints use cursor-based pagination. Pass the cursor value from a previous response as the after query parameter to retrieve the next page. Use the limit parameter to control page size (default: 20, maximum: 100).
# First page
curl "https://api.openanalyst.com/v1/reports?limit=25" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Next page
curl "https://api.openanalyst.com/v1/reports?limit=25&after=cur_xyz789" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"When pagination.has_more is false, you have reached the last page. The total field reflects the total number of matching records and does not change between pages.
Rate Limiting
API rate limits are applied per API key. The default limits are:
| Plan | Requests / minute | Requests / day |
|---|---|---|
| Starter | 60 | 10,000 |
| Pro | 300 | 100,000 |
| Enterprise | Custom | Custom |
Every response includes the following rate limit headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 247
X-RateLimit-Reset: 1732016460When the limit is exceeded, the API returns HTTP 429 with a Retry-Afterheader indicating the number of seconds to wait before retrying.
Error Response Format
All errors return a consistent JSON body regardless of the error type:
{
"error": {
"code": "not_found",
"message": "Dataset ds_nonexistent does not exist or you do not have access to it.",
"status": 404,
"request_id": "req_8f3kd92ls"
}
}| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | invalid_request | Missing or malformed request parameters |
| 401 | authentication_required | Missing or invalid API key |
| 403 | forbidden | Key does not have permission for this action |
| 404 | not_found | The requested resource does not exist |
| 409 | conflict | Resource already exists or state conflict |
| 422 | unprocessable | Semantically invalid request body |
| 429 | rate_limit_exceeded | Too many requests |
| 500 | internal_error | Unexpected server error |
Datasets
Datasets represent connected data sources or uploaded data files.
GET /datasets
List all datasets accessible to the authenticated key.
curl "https://api.openanalyst.com/v1/datasets" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Response
{
"data": [
{
"id": "ds_abc123",
"name": "Production Postgres",
"type": "postgresql",
"status": "connected",
"created_at": "2025-09-01T12:00:00Z"
}
],
"pagination": { "cursor": null, "has_more": false, "total": 1 }
}POST /datasets
Create (connect) a new dataset.
curl -X POST "https://api.openanalyst.com/v1/datasets" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Analytics Warehouse",
"type": "bigquery",
"credentials": {
"project_id": "my-gcp-project",
"dataset": "analytics",
"service_account_key": "{ ... }"
}
}'GET /datasets/:id
Retrieve a single dataset by ID.
curl "https://api.openanalyst.com/v1/datasets/ds_abc123" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"DELETE /datasets/:id
Disconnect and remove a dataset. This does not delete the underlying data source.
curl -X DELETE "https://api.openanalyst.com/v1/datasets/ds_abc123" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Response: HTTP 204 No ContentQueries
Queries execute SQL or natural-language requests against a connected dataset.
POST /queries/run
curl -X POST "https://api.openanalyst.com/v1/queries/run" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset_id": "ds_abc123",
"sql": "SELECT country, COUNT(*) AS users FROM customers GROUP BY country ORDER BY 2 DESC LIMIT 10",
"timeout": 60
}'
# Response
{
"id": "qry_xyz789",
"status": "running",
"created_at": "2025-11-20T10:00:00Z"
}GET /queries/:id/results
Retrieve the results of a completed query. Poll this endpoint until status is completed or failed.
curl "https://api.openanalyst.com/v1/queries/qry_xyz789/results" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Response
{
"id": "qry_xyz789",
"status": "completed",
"columns": ["country", "users"],
"rows": [
{ "country": "United States", "users": 48291 },
{ "country": "United Kingdom", "users": 12047 }
],
"row_count": 10,
"execution_time_ms": 342
}Reports
GET /reports
curl "https://api.openanalyst.com/v1/reports?limit=10" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"POST /reports
curl -X POST "https://api.openanalyst.com/v1/reports" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Monthly Revenue Report",
"dataset_id": "ds_abc123",
"format": "pdf",
"schedule": {
"frequency": "monthly",
"day_of_month": 1,
"hour": 7,
"timezone": "UTC"
},
"recipients": ["finance@example.com"]
}'GET /reports/:id
curl "https://api.openanalyst.com/v1/reports/rpt_abc123" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"PUT /reports/:id
Full replacement update of a report. All writable fields must be included.
curl -X PUT "https://api.openanalyst.com/v1/reports/rpt_abc123" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Monthly Revenue Report (Updated)",
"dataset_id": "ds_abc123",
"format": "xlsx",
"schedule": { "frequency": "monthly", "day_of_month": 2, "hour": 8, "timezone": "UTC" },
"recipients": ["finance@example.com", "cfo@example.com"]
}'Agents
POST /agents/run
Start a new AI agent session. The agent will autonomously query data, reason over results, and produce a final answer.
curl -X POST "https://api.openanalyst.com/v1/agents/run" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "What were the top 5 product categories by revenue last quarter, and how do they compare to the same quarter last year?",
"dataset_ids": ["ds_abc123"],
"model": "claude-3-5-sonnet",
"max_steps": 15
}'
# Response
{
"id": "ses_kj2h8d",
"status": "running",
"created_at": "2025-11-20T10:05:00Z"
}GET /agents/sessions
curl "https://api.openanalyst.com/v1/agents/sessions" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"GET /agents/sessions/:id
curl "https://api.openanalyst.com/v1/agents/sessions/ses_kj2h8d" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Response (completed session)
{
"id": "ses_kj2h8d",
"status": "completed",
"prompt": "What were the top 5 product categories ...",
"model": "claude-3-5-sonnet",
"steps": 8,
"final_answer": "The top 5 product categories by revenue last quarter were ...",
"created_at": "2025-11-20T10:05:00Z",
"completed_at": "2025-11-20T10:05:42Z"
}Exports and Webhooks
POST /exports
curl -X POST "https://api.openanalyst.com/v1/exports" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset_id": "ds_abc123",
"format": "parquet",
"filter": {
"date_range": { "start": "2025-01-01", "end": "2025-12-31" }
}
}'GET /exports/:id/download
Returns a temporary pre-signed download URL valid for 15 minutes. The export must have status: "ready".
curl "https://api.openanalyst.com/v1/exports/exp_mn4op9/download" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Response
{
"download_url": "https://storage.openanalyst.com/exports/exp_mn4op9.parquet?token=...",
"expires_at": "2025-11-20T10:20:00Z",
"size_bytes": 4820194,
"format": "parquet"
}POST /webhooks
curl -X POST "https://api.openanalyst.com/v1/webhooks" \
-H "Authorization: Bearer $OPENANALYST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourdomain.com/hooks/openanalyst",
"events": ["report.completed", "agent.session.completed", "export.ready"],
"secret": "whsec_your_signing_secret"
}'GET /webhooks and DELETE /webhooks/:id
# List webhooks
curl "https://api.openanalyst.com/v1/webhooks" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"
# Delete a webhook
curl -X DELETE "https://api.openanalyst.com/v1/webhooks/wh_pq5rs1" \
-H "Authorization: Bearer $OPENANALYST_API_KEY"Tip: Webhook payloads include an X-OpenAnalyst-Signature header containing an HMAC-SHA256 signature of the raw request body using your webhook secret. Always verify this signature before processing the event.