Atlas API
A high-performance timeseries data platform for storing, querying, and streaming arbitrary timeseries data. Built with Go and TimescaleDB.
Quick Start
Reading Data
# First-time API users: acquire the "public" tag (Atlas UI does this automatically)
curl -X POST https://your-domain.com/api/tags/acquire/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tag_name": "public"}'
# List available datasets
curl -H "X-API-Key: YOUR_API_KEY" https://your-domain.com/api/datasets/
# Search by name or description
curl -H "X-API-Key: YOUR_API_KEY" \
"https://your-domain.com/api/datasets/?search=bitcoin"
# Get data from a dataset
curl -H "X-API-Key: YOUR_API_KEY" \
"https://your-domain.com/api/rows/?dataset=1&limit=100"
Creating Data
# 1. Create a dataset
curl -X POST https://your-domain.com/api/datasets/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "my_btc_prices", "description": "Bitcoin hourly prices"}'
# 2. Insert data (use the returned dataset ID)
curl -X POST https://your-domain.com/api/rows/bulk_insert/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset": 1, "rows": [
{"timestamp": "2025-12-16T10:00:00Z", "values": {"price": 104500}},
{"timestamp": "2025-12-16T11:00:00Z", "values": {"price": 104600}}
]}'
# 3. Share it by adding the "public" tag
curl -X POST https://your-domain.com/api/datasets/1/tags/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tag_name": "public"}'
Authentication
All API requests require authentication using an API key passed in the X-API-Key header.
curl -H "X-API-Key: YOUR_API_KEY" https://your-domain.com/api/datasets/
Never expose your API key in client-side code or public repositories.
Health Check (No Auth Required)
Check if the service is running and connected to the database.
{
"status": "healthy",
"time": "2025-12-16T10:30:00Z",
"database": "connected"
}
Access Control & Roles
Atlas uses a tag-based authorization system combined with dataset ownership. Understanding this system is essential for sharing and accessing data.
Creators: You own what you create. Assign tags to share datasets.
Consumers: Acquire tags to access datasets. The public tag is free.
Rule: Matching tags = read access. Ownership = full control.
Ownership
When you create a dataset, you become its owner with full control: read, write, delete, and manage access tags. New datasets are private by default—only visible to the owner until tags are assigned.
Tag-Based Access
Users can read a dataset if they have at least one tag that matches a tag on the dataset. No matching tags means no access (unless you're the owner).
Tag Types
| Tag Type | How to Acquire | Use Case |
|---|---|---|
| Public Tags | Any user can acquire freely | public, test - Open datasets for everyone |
| Private Tags | Require a secret key to acquire | premium, tiingo - Restricted data access |
Permissions Matrix
| Action | Owner | Matching Tag | No Match |
|---|---|---|---|
| Read dataset / rows | ✅ | ✅ | ❌ |
| Write / Update / Delete | ✅ | ❌ | ❌ |
| Manage tags | ✅ | ❌ | ❌ |
List & Search Datasets
Text Search
List datasets with optional text search. The search parameter performs case-insensitive substring matching against the dataset name and description.
| Parameter | Type | Description |
|---|---|---|
search |
string | Substring to match against name or description (case-insensitive) |
limit |
integer | Max results to return (default: 250) |
offset |
integer | Number of results to skip (for pagination) |
Examples
# Search for datasets containing "bitcoin" in name or description
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/?search=bitcoin"
# Search for datasets with "price" anywhere in the text
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/?search=price"
# Get first 10 results
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/?search=market&limit=10"
The web interface search bar uses this endpoint—substring matching against name and description.
Metadata Search
Search datasets by metadata field values. Each query parameter (except limit and offset) is treated as a metadata filter with exact matching.
| Parameter | Type | Description |
|---|---|---|
{key} |
any | Metadata field name and value (exact match) |
limit |
integer | Max results to return (default: 250) |
offset |
integer | Number of results to skip |
Examples
# Find all datasets from Tiingo
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/search/?source=tiingo"
# Find BTC/USD data with 1-minute frequency
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/search/?ticker=btcusd&frequency=1min"
# Find premium-tier datasets
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/search/?tier=premium"
At least one metadata filter is required. Use /api/datasets/ to list all datasets.
Create / Read / Update / Delete
Create a new dataset. You automatically become the owner.
{
"name": "btc_hourly_ohlcv",
"description": "Bitcoin hourly OHLCV candles from Tiingo",
"metadata": {
"source": "tiingo",
"ticker": "btcusd",
"frequency": "1h"
}
}
{
"id": 1,
"name": "btc_hourly_ohlcv",
"description": "Bitcoin hourly OHLCV candles from Tiingo",
"metadata": {"source": "tiingo", "ticker": "btcusd", "frequency": "1h"},
"columns": [],
"created_at": "2025-12-16T10:30:00Z",
"updated_at": "2025-12-16T10:30:00Z"
}
Get a single dataset by ID. Requires read access (owner or matching tag).
Update dataset name, description, and metadata. Replaces all fields.
Permanently delete a dataset and all its data. This cannot be undone.
Metadata Operations
Update metadata without affecting other dataset fields.
Merge new fields into existing metadata (partial update).
curl -X PATCH https://your-domain.com/api/datasets/1/metadata/ \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"version": "2.0"}'
# Merges with existing: {"source": "tiingo"} → {"source": "tiingo", "version": "2.0"}
Replace all metadata (full overwrite).
Columns
Columns define the schema of your timeseries data. Each column has a name and data type.
You can insert data without pre-defining columns. The values field accepts any JSON object. Columns are primarily for schema documentation and Atlas UI display.
List columns for a specific dataset.
Create a new column definition. Use this to document your schema for the Atlas UI.
{
"dataset": 1,
"name": "close_price",
"dtype": "float"
}
| Field | Type | Description |
|---|---|---|
dataset |
integer | Dataset ID this column belongs to |
name |
string | Column name (should match keys in row values) |
dtype |
string | float or int |
Rows (Timeseries Data)
Rows contain the actual timeseries data. Each row has a timestamp and values for the dataset's columns.
Query timeseries data with optional filters.
| Parameter | Type | Description |
|---|---|---|
dataset |
integer | Filter by dataset ID (required for access check) |
start |
datetime | Start timestamp (ISO 8601 format) |
end |
datetime | End timestamp (ISO 8601 format) |
ordering |
string | timestamp (ascending) or -timestamp (descending) |
limit |
integer | Max rows to return (default: 250) |
offset |
integer | Number of rows to skip |
Example
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/rows/?dataset=1&start=2025-12-01T00:00:00Z&end=2025-12-16T00:00:00Z&ordering=-timestamp&limit=100"
Insert a single row of data.
{
"dataset": 1,
"timestamp": "2025-12-16T10:00:00Z",
"values": {
"open": 104500.00,
"high": 104800.00,
"low": 104200.00,
"close": 104600.00,
"volume": 1234567
}
}
Bulk insert using PostgreSQL COPY protocol. Optimized for ~100K rows/sec throughput.
{
"dataset": 1,
"rows": [
{
"timestamp": "2025-12-16T10:00:00Z",
"values": {"open": 104500, "close": 104600}
},
{
"timestamp": "2025-12-16T11:00:00Z",
"values": {"open": 104600, "close": 104700}
}
]
}
{
"inserted": 2,
"total": 2
}
Streaming & Downloads
Efficient endpoints for bulk data access and real-time streaming.
Download all data from a dataset in CSV or JSON format.
| Parameter | Type | Description |
|---|---|---|
dataset |
integer | Dataset ID (required) |
output |
string | json (default) or csv |
start |
datetime | Optional start timestamp |
end |
datetime | Optional end timestamp |
# Download as CSV
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/rows/bulk_download/?dataset=1&output=csv" \
> data.csv
# Download as JSON
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/rows/bulk_download/?dataset=1&output=json" \
> data.json
Bulk download recent data as NDJSON (newline-delimited JSON). Memory-efficient for large datasets.
# Download most recent 1000 rows
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/rows/live/?dataset=1&limit=1000"
# Output: one JSON object per line
# {"timestamp":"2025-12-16T10:00:00Z","close":104600}
# {"timestamp":"2025-12-16T11:00:00Z","close":104700}
Real-time Server-Sent Events (SSE) stream. Connection stays open and pushes new data as it arrives.
| Parameter | Type | Description |
|---|---|---|
dataset |
integer | Dataset ID (required) |
poll_interval |
integer | Seconds between checks for new data (default: 2) |
Event Types
connected- Initial connection establisheddata- New data point receivedheartbeat- Keepalive (no new data)error- Query error occurred
# Connect to real-time stream
curl -H "X-API-Key: YOUR_KEY" \
-H "Accept: text/event-stream" \
"https://your-domain.com/api/rows/stream/?dataset=1&poll_interval=2"
# Output (connection stays open):
# event: connected
# data: {"dataset_id":1,"message":"Connected to real-time stream"}
#
# event: data
# data: {"timestamp":"2025-12-16T10:00:00Z","close":104600}
#
# event: heartbeat
# data: {"timestamp":"2025-12-16T10:00:02Z"}
Error Responses
All errors return a JSON object with an error field and optional details.
{
"error": "Dataset not found",
"details": "No dataset with ID 999"
}
| Status Code | Meaning |
|---|---|
400 |
Bad Request - Invalid parameters or request body |
401 |
Unauthorized - Missing or invalid API key |
403 |
Forbidden - You don't have permission for this action |
404 |
Not Found - Resource doesn't exist |
429 |
Too Many Requests - Rate limit exceeded |
500 |
Internal Server Error - Something went wrong |
Common 403 Forbidden Scenarios
- "No matching tags" - You need to acquire a tag that's assigned to the dataset
- "Owner access required" - Only the dataset creator can perform write operations
- "Tag secret required" - The tag is private and requires a secret key
Pagination
List endpoints return paginated results with metadata for navigation.
{
"count": 150,
"next": "https://your-domain.com/api/datasets/?limit=50&offset=50",
"previous": null,
"results": [...]
}
| Field | Description |
|---|---|
count |
Total number of results matching the query |
next |
URL for the next page (null if no more results) |
previous |
URL for the previous page (null if on first page) |
results |
Array of items for the current page |
Pagination Parameters
limit- Number of results per page (default: 250)offset- Number of results to skip
# Get page 2 with 50 items per page
curl -H "X-API-Key: YOUR_KEY" \
"https://your-domain.com/api/datasets/?limit=50&offset=50"
Troubleshooting
Empty dataset list
You need the public tag to see public datasets. The Atlas UI acquires this automatically on login. API-only users should run:
curl -X POST https://your-domain.com/api/tags/acquire/ \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"tag_name": "public"}'
Others can't see my dataset
New datasets are private. Assign a tag to share:
curl -X POST https://your-domain.com/api/datasets/YOUR_DATASET_ID/tags/ \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"tag_name": "public"}'
403 Forbidden on read
You lack matching tags. Check what tags the dataset requires (ask the owner), list available tags with GET /api/tags/, then acquire the matching tag.
403 Forbidden on write
Only the owner can write. Tags grant read access only. Create your own dataset if you need write access.
Check your access
curl -X POST https://your-domain.com/api/access/check/ \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"dataset_id": 1}'
# Returns: {"has_access": true, "is_owner": false, "matching_tags": ["public"]}
Allora Atlas Team