Reference

REST API

Base URL: https://api.attoscloud.com/api/v1. All endpoints return JSON. Binary blob delivery uses a dedicated sync endpoint consumed directly by SDK Synchronizers.

Authentication

Pass your API key in the X-API-Key header on every request. Keys are generated in the dashboard under Settings → API Keys.

bash
curl https://api.attoscloud.com/api/v1/datasets \
  -H "X-API-Key: at_live_9f3a21b47c02"

GET /datasets/sync/:dataset_id

Streams the compiled .attos binary for the specified dataset. This is the endpoint polled by SDK Synchronizers on every sync cycle. The response body is the raw binary; a X-Attos-Config response header carries a JSON object controlling SDK-side settings such as telemetry opt-in state.

bash
curl -o data.attos \
  -H "X-Attos-Node-ID: $(hostname)" \
  -H "X-API-Key: $ATTOS_KEY" \
  "https://api.attoscloud.com/api/v1/datasets/sync/ds_a1b2c3d4"
StatusMeaning
200 OKBody is the raw .attos binary, with an ETag. Validate, then mmap it.
304 Not ModifiedIf-None-Match matched the current ETag: nothing changed (0 bytes).
400 Bad RequestMissing X-Attos-Node-ID header.
401 UnauthorizedMissing or invalid X-API-Key.
402 Payment RequiredA plan limit was reached: node_limit, bandwidth_limit or overage_disabled. See Plans, limits & error codes.
404 Not Founddataset_id does not exist in this project.
409 ConflictThe dataset is still compiling (not ready to sync).

PUT /datasets/:id/data

Upload new CSV or NDJSON source data for an existing dataset. The control plane runs the Go builder (Builder.BuildFromCSV or Builder.BuildFromNDJSON), compiles a new .attos binary, stores it atomically in object storage (GCS), and the next SDK sync poll picks it up automatically. Rejected uploads (402/429) change nothing.

bash
curl -X PUT \
  "https://api.attoscloud.com/api/v1/datasets/ds_a1b2c3d4/data" \
  -H "X-API-Key: $ATTOS_KEY" \
  -F "file=@users.csv"

Response

json
{
  "id":        "6f1c2a9e-4b7d-4a8e-9c1f-2d3e4f5a6b7c",
  "name":      "users",
  "row_count": 1000,
  "blob_size": 32768,
  "status":    "ready"
}

Rate limits

Only the sign-up, login, password-reset, lead and checkout endpoints are rate limited, per client IP (2 requests per second, bursts of 5). Over that they answer 429 with {"error": "Too many requests. Please try again later."} and no Retry-After header. The dataset and sync endpoints have no request-rate limit; they are bound by the plan limits below.

Plans, limits & error codes

Limits belong to the project. Monthly limits reset on the 1st of each month (UTC). Pro and Enterprise projects choose overage separately for dataset updates, active nodes and sync bandwidth (Billing settings; all on by default). A meter with overage on keeps working past its included amount and the extra usage is billed; with it off, that meter stops at the included amount like Free, with the code overage_disabled.

LimitFreeStarter ($29/mo)Pro ($99/mo)Enterprise
Datasets per project520UnlimitedPer contract (unlimited if not set)
Upload size (CSV/NDJSON)10 MB100 MBUnlimitedPer contract (unlimited if not set)
Dataset updates / month10200 included, then +$0.02/update1,000 included, then +$0.01/updatePer contract (unlimited if not set)
Active edge nodes310 included, then +$3/node/mo (monthly average)50 included, then +$2/node/mo (monthly average)Per contract (unlimited if not set)
Sync bandwidth / month10 GB20 GB included, then +$0.15/GB50 GB included, then +$0.15/GBPer contract (unlimited if not set)
At the limitHard stop (402 / 429)Hard stop by default; metered overage per meter if switched onMetered overage, or hard stop with overage offMetered overage, or hard stop with overage off

Active nodes are distinct X-Attos-Node-IDs that synced in the last 5 minutes. Extra nodes are billed once a month on the average of the month's hourly node counts. Bandwidth counts the bytes of sync responses; a 304 counts as zero.

Error body

Every plan-limit rejection has the same JSON shape. error is a human-readable message, code is stable, limit_kind names the limit (it differs from code only for overage_disabled). A 429 also carries Retry-After: the seconds until the monthly reset.

json
{
  "error":       "Free projects include 10 GB of sync bandwidth per month and it is used up. ...",
  "code":        "bandwidth_limit",
  "status":      402,
  "limit_kind":  "bandwidth_limit",
  "limit":       10000000000,
  "used":        10000482113,
  "upgrade_url": "/dashboard/billing"
}
StatusCodeWhen it happensWhat the SDK doesHow to fix it
402dataset_limitCreating a dataset when the project already has its maximum number of datasets.Not used by SDKs (compile endpoint).Delete a dataset, or upgrade to Pro (unlimited datasets).
402upload_too_largeAn upload (create or update) is larger than the plan's upload size (Free: 10 MB). Nothing is created or stored.Not used by SDKs (compile endpoint).Upload a smaller file, or upgrade to Pro (no upload size limit).
429update_limitA Free project has used its dataset updates for this month. Retry-After = seconds until the month resets (UTC).Not used by SDKs (update endpoint).Wait for the monthly reset, or upgrade to Pro.
402node_limitA sync from a node beyond the Free plan's active nodes (nodes seen in the last 5 minutes).Keeps serving its current data; pauses sync for 5 minutes (or Retry-After, capped at 1 hour) and retries.Stop unused nodes, or upgrade to Pro.
402bandwidth_limitA Free project has used its sync bandwidth for this month. Unchanged data still gets 304.Keeps serving its current data; pauses sync for 5 minutes (or Retry-After, capped at 1 hour) and retries.Wait for the monthly reset, or upgrade to Pro.
402 or 429overage_disabledA paid project with overage switched off for that meter reached an included limit (limit_kind says which: update_limit is sent as 429, node_limit and bandwidth_limit as 402).On sync: keeps serving its current data and backs off like node_limit.Switch on overage for that meter in Billing settings, raise the contract limit (Enterprise), or wait for the monthly reset.

POST /datasets/compile

Create a new dataset from source data in a single request. The attos_value column is mandatory; all other columns are treated as composite key dimensions and sorted alphabetically.

bash
curl -X POST https://api.attoscloud.com/api/v1/datasets/compile \
  -H "X-API-Key: $ATTOS_KEY" \
  -F "file=@users.ndjson" \
  -F "name=edge-router"
json
{
  "id":        "0b7e5c3d-2f1a-4c6b-8d9e-1a2b3c4d5e6f",
  "name":      "edge-router",
  "row_count": 12400000,
  "blob_size": 32505856,
  "status":    "ready"
}