DataStoryBot API Reference: Endpoints, Parameters, and Responses
Complete API reference for DataStoryBot. Endpoints, request/response schemas, parameters, error codes, and rate limits for CSV analysis.
DataStoryBot API Reference: Endpoints, Parameters, and Responses
This is the complete reference for the DataStoryBot API. Four endpoints, no authentication during the open beta, and a linear pipeline: upload a CSV, analyze it, refine a story, download the outputs.
If you want a guided walkthrough instead, start with Getting Started with the DataStoryBot API and come back here when you need the specifics.
Base URL
https://datastory.bot
All endpoints are relative to this base. No versioning prefix — the API is in open beta and the contract is stable but not yet frozen.
Authentication
No API key is required during the current open beta. When auth is introduced, it will use a Bearer token:
Authorization: Bearer dsb_your_api_key
Check the DataStoryBot playground for the latest auth requirements.
Containers
Every session starts with a container — an ephemeral OpenAI Code Interpreter environment running GPT-4o. It holds your uploaded file, generated charts, filtered datasets, and the AI's execution state.
Container TTL: 20 minutes. After that, the container and all files are permanently deleted. No extension, no recovery. The containerId from the upload response is your session handle for all subsequent calls.
POST /api/upload
Uploads a CSV file and creates a new container.
Content-Type: multipart/form-data
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | CSV file, max 50 MB |
curl -X POST https://datastory.bot/api/upload \
-F "file=@sales_data.csv"
with open("sales_data.csv", "rb") as f:
resp = requests.post(
"https://datastory.bot/api/upload",
files={"file": ("sales_data.csv", f, "text/csv")}
)
result = resp.json()
Response — 200
{
"containerId": "ctr_abc123def456",
"fileId": "file-7x8y9z",
"metadata": {
"fileName": "sales_data.csv",
"rowCount": 12840,
"columnCount": 9,
"columns": ["date", "region", "product", "revenue", "units",
"cost", "channel", "customer_segment", "returns"]
}
}
| Field | Type | Description |
|---|---|---|
containerId | string | Session handle. Pass to all subsequent calls. |
fileId | string | Identifier for the uploaded file. |
metadata.fileName | string | Original filename. |
metadata.rowCount | integer | Data rows (excluding header). |
metadata.columnCount | integer | Columns detected. |
metadata.columns | string[] | Column names from the header. |
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_file | No file provided, or not valid CSV. |
| 413 | file_too_large | Exceeds 50 MB limit. |
| 429 | rate_limit_exceeded | Too many requests. |
| 500 | execution_error | Container creation failed. |
POST /api/analyze
Discovers three story angles in the uploaded data. The AI reads your CSV, runs exploratory Python code inside the container, and returns structured results.
Content-Type: application/json
| Parameter | Type | Required | Description |
|---|---|---|---|
containerId | string | Yes | From the upload response. |
steeringPrompt | string | No | Guides the AI toward a specific topic. Does not force output — it weights certain patterns more heavily. |
curl -X POST https://datastory.bot/api/analyze \
-H "Content-Type: application/json" \
-d '{
"containerId": "ctr_abc123def456",
"steeringPrompt": "Focus on regional differences in return rates"
}'
Response — 200
{
"stories": [
{
"title": "Q4 Revenue Surge Driven by Enterprise Segment",
"summary": "Enterprise customers accounted for 68% of Q4 revenue growth, with a 42% QoQ increase concentrated in APAC.",
"chartFileId": "file-chart001"
},
{
"title": "Rising Return Rates Signal Product Quality Issues",
"summary": "Returns increased 23% over 6 months. The Pro line shows 3x the return rate of other lines.",
"chartFileId": "file-chart002"
},
{
"title": "Direct Channel Overtakes Retail for First Time",
"summary": "DTC sales surpassed retail revenue in March, driven by a 31% increase in repeat purchases.",
"chartFileId": "file-chart003"
}
]
}
| Field | Type | Description |
|---|---|---|
stories | array | Always exactly 3 story objects. |
stories[].title | string | Headline for the story. Pass this exact string to /api/refine. |
stories[].summary | string | 1-2 sentence summary with data points. |
stories[].chartFileId | string | Preview chart. Download via /api/files. |
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Missing or invalid containerId. |
| 404 | container_expired | Container TTL elapsed. |
| 429 | rate_limit_exceeded | Too many requests. |
| 500 | execution_error | Analysis failed inside container. |
POST /api/refine
Generates a full narrative, charts, and filtered dataset for the selected story.
Content-Type: application/json
| Parameter | Type | Required | Description |
|---|---|---|---|
containerId | string | Yes | From the upload response. |
selectedStoryTitle | string | Yes | Exact title from an analyze response story. |
refinementPrompt | string | No | Adjusts tone, length, format, or emphasis. |
curl -X POST https://datastory.bot/api/refine \
-H "Content-Type: application/json" \
-d '{
"containerId": "ctr_abc123def456",
"selectedStoryTitle": "Q4 Revenue Surge Driven by Enterprise Segment",
"refinementPrompt": "Executive summary. Under 300 words. Emphasize APAC."
}'
Response — 200
{
"narrative": "## Q4 Revenue Surge Driven by Enterprise Segment\n\nEnterprise customers drove 68% of Q4 growth...",
"charts": [
{
"fileId": "file-chart101",
"caption": "Quarterly revenue by customer segment (2025)"
},
{
"fileId": "file-chart102",
"caption": "Enterprise revenue growth by region"
}
],
"resultDataset": {
"fileId": "file-ds001",
"caption": "Filtered dataset: Enterprise segment transactions Q4 2025"
}
}
| Field | Type | Description |
|---|---|---|
narrative | string | Markdown-formatted narrative, typically 300-500 words. |
charts | array | 2-4 chart objects with fileId and caption. |
resultDataset | object | Filtered CSV with rows relevant to the story. |
resultDataset.fileId | string | Download via /api/files. |
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_story_title | Title does not match any story from the analyze step. |
| 404 | container_expired | Container TTL elapsed. |
| 429 | rate_limit_exceeded | Too many requests. |
| 500 | execution_error | Refinement failed inside container. |
GET /api/files//
Downloads a file from the container — chart PNGs or filtered CSVs.
# Download a chart
curl -o chart.png \
"https://datastory.bot/api/files/ctr_abc123def456/file-chart101"
# Download a filtered dataset
curl -o filtered.csv \
"https://datastory.bot/api/files/ctr_abc123def456/file-ds001"
Response — 200
Raw file content. Content-Type varies:
| File type | Content-Type |
|---|---|
| Chart | image/png |
| Dataset | text/csv |
Chart specifications
| Property | Value |
|---|---|
| Format | PNG, transparent background |
| Resolution | 150 DPI |
| Canvas | 10 x 6 inches (1500 x 900 px) |
| Background | Dark theme (#141414) |
| Text | #ffffff, #cccccc, #999999 |
Errors
| Status | Code | Meaning |
|---|---|---|
| 404 | container_expired or file_not_found | Container expired or file ID invalid. |
| 429 | rate_limit_exceeded | Too many requests. |
Rate Limits
During open beta:
| Resource | Limit |
|---|---|
| Uploads per minute | 10 |
| Analyze calls per minute | 10 |
| Refine calls per minute | 10 |
| File downloads per minute | 60 |
| Concurrent containers | 5 |
Rate limit responses include a Retry-After header:
{
"error": "rate_limit_exceeded",
"message": "Upload limit exceeded. Retry after 12 seconds.",
"retryAfter": 12
}
Error Response Format
All errors follow this structure:
{
"error": "container_expired",
"message": "Container ctr_abc123def456 has expired. Upload again to create a new container."
}
The error field is a machine-readable code. The message field is human-readable. Rate limit errors also include retryAfter (integer, seconds until retry).
Error codes
| Code | Meaning |
|---|---|
invalid_file | Not valid CSV or empty. |
file_too_large | Exceeds 50 MB. |
container_expired | 20-minute TTL elapsed. |
container_not_found | Container ID does not exist. |
invalid_story_title | Title does not match analyze output. |
invalid_request | Missing required parameters. |
rate_limit_exceeded | Check retryAfter. |
execution_error | Code execution failed. Retry once. |
Next Steps
- Getting Started with the DataStoryBot API — step-by-step walkthrough with Python, JavaScript, and curl
- How to Use AI to Analyze Your Data — the conceptual framework behind AI-powered analysis
- DataStoryBot playground — test the full flow in a browser
Ready to find your data story?
Upload a CSV and DataStoryBot will uncover the narrative in seconds.
Try DataStoryBot →