API reference

Everything in the app is available over HTTPS with JSON. Create a key in Account settings → API keys and send it as a bearer token. Keys act as you; they can't manage keys, delivery buckets or your account.

curl https://useground.dev/api/datasets \
  -H "Authorization: Bearer gk_…"

Python

The SDK wraps the whole flow: upload (with multipart for large files), wait for the analysis, export, download. Install it from the repository's sdk/python folder.

pip install ./sdk/python   # or: pip install "ground @ git+https://github.com/<org>/ground#subdirectory=sdk/python"

from ground import Ground

g = Ground(api_key="gk_…", base_url="https://useground.dev")
ds = g.upload("patients.parquet", privacy_epsilon=None).wait()
print(ds.report["scores"])                      # quality and privacy before you export
job = ds.export(rows=1_000_000, format="parquet", options={"classMix": {'"yes"': 0.5, '"no"': 0.5}})
job.wait().download("synthetic_patients.parquet")
df = job.to_pandas()                            # or straight into pandas

Errors and limits

Errors are JSON { "error": "…" } with a status: 400 invalid input, 401 missing or revoked key, 402 not enough credits (the message says how many are needed and left), 403 not allowed (or your organization requires two-factor), 404 not found or not yours, 409 not ready yet, 429 too many exports running (3 at a time).

Upload or import data

Files go straight to storage: reserve a key, PUT the bytes (one request, or in parts above 64 MB), then create the dataset. Or skip the upload and give links.

POST/api/datasets/preflight

Optional, before sending any bytes: checks the upload against your plan's size limit (413), related tables on your plan (403) and credits for the analysis (402). Nothing is charged.

Body
{ "files": [{ "bytes": 52428800 }], "imports": 0 }
Returns
{ "credits": 5, "available": 95, "uploadCapBytes": 1073741824 }
POST/api/uploads

Reserve a storage key for a file (CSV, TSV, Excel, JSON, JSON Lines or Parquet).

Body
{ "filename": "patients.csv", "bytes": 52428800 }
Returns
{ "key": "uploads/…/patients.csv", "url": "https://…", "headers": { "Content-Type": "text/csv" } }
// or, above 64 MB:
{ "key": "…", "multipart": { "uploadId": "…", "partSize": 16777216, "urls": ["https://…part1", "…"] } }
PUT<url>

Send the file (or each part, in order, to its URL) with the returned headers. Keep each part's ETag response header.

POST/api/uploads/complete

Finish a multipart upload.

Body
{ "key": "…", "uploadId": "…", "parts": [{ "partNumber": 1, "etag": "\"9b2c…\"" }] }
POST/api/datasets

Create a dataset from uploads and/or links, and start the analysis. Several files are learned as related tables.

Body
{
  "uploads": [{ "key": "uploads/…/patients.csv", "filename": "patients.csv" }],
  "imports": [{ "url": "https://bucket.s3.amazonaws.com/visits.parquet?X-Amz-Signature=…" }],
  "privacy": { "epsilon": 3 },          // optional: (ε, δ)-differential privacy, single tables
  "allowAiExamples": true               // let Claude see scrubbed examples of text columns
}
Returns
{ "id": "8d0f…" }

Datasets

GET/api/datasets

Your datasets and those shared with your organizations.

GET/api/datasets/{id}

Status (analyzing, ready, failed), live progress while analyzing, and once ready the columns, label, settings, model version and quality/privacy scores.

Returns
{ "status": "ready", "modelVersion": 3, "rows": 48842, "target": "income",
  "columns": [{ "name": "age", "role": "model", "type": "number" }, …],
  "report": { "scores": { "overall": 91, … }, "privacy": { "verdict": "pass", … } },
  "options": { "rows": 10000, "seed": 1, … } }
POST/api/datasets/{id}/refit

Analyze again with a different label, column roles, privacy budget, usefulness targets or entity column.

Body
{ "target": "readmitted", "roles": { "notes": "text", "mrn": "id" }, "privacy": null, "utilityTargets": ["los_days"], "entityColumn": null }
PATCH/api/datasets/{id}

Save extension settings (see Options below), or rename.

Body
{ "options": { … }, "name": "Readmissions v2" }
POST/api/datasets/{id}/preview

25 rows for these options, with text columns written.

Body
{ "options": { … } }
Returns
{ "rows": [ … ] }
GET/api/datasets/{id}/versions

Every analysis of the dataset; which one is active.

POST/api/datasets/{id}/versions/{version}

Pin a version: previews and exports use it until the next analysis.

GET/api/datasets/{id}/versions/{version}/model

Download the learned model as JSON (derived from your data: treat it like the source).

POST/api/datasets/{id}/org

Share with one of your organizations, or stop sharing.

Body
{ "orgId": "org_…" }  // or null
GET/api/datasets/{id}/source

Download the original upload (the main table's, for related tables) while it is kept. Owner only; 410 once it has been deleted or has passed its retention date.

DELETE/api/datasets/{id}/source

Delete the original upload(s) now. The learned model stays, so previews and exports keep working; re-analyzing doesn't.

DELETE/api/datasets/{id}

Move the dataset to the trash. It can be restored until it is purged with all its files (see the trash routes); its exports can't be downloaded meanwhile.

GET/api/datasets/trash

Datasets in the trash, with when each is purged for good. Those deleted before the trash existed have "legacy": true and no purge date: they can't be restored, only deleted forever.

POST/api/datasets/trash/{id}

Restore a dataset from the trash (409 for a legacy one).

DELETE/api/datasets/trash/{id}

Delete a trashed dataset forever: its uploads, training copies, models, reports and exports.

Exports

POST/api/jobs

Generate rows in the background as CSV, JSON Lines or Parquet. Related tables arrive as a zip with one file per table. Charges credits when it starts (the job's credits field); a failed export is refunded.

Body
{ "datasetId": "8d0f…", "format": "parquet", "options": { "rows": 1000000, … }, "destinationId": null }
Returns
{ "job": { "id": "…", "status": "queued", "rowsTotal": 1000000, … } }
GET/api/jobs/{id}

Status, rows done, the worker's current step, delivery status and model version.

GET/api/jobs/{id}/download

Redirects to a short-lived download link for a finished file.

POST/api/jobs/{id}/cancel

Stop a queued or running export.

GET/api/jobs

Your recent exports.

GET/api/destinations

Your delivery buckets (added in Account settings), for destinationId.

GET/api/credits

Your credit balance (this month's allowance and purchased credits), recent charges and refunds, and the price rules.

Returns
{ "balance": { "available": 3870, "monthlyLeft": 2870, "purchased": 1000, "resetsAt": "2026-11-01T00:00:00Z" }, "history": [ … ] }

Designed datasets

Datasets made from a written request instead of an upload.

POST/api/datasets

Start one from a template or an empty spec.

Body
{ "templateId": "ecommerce" }  // or { "name": "Support tickets" }
POST/api/datasets/{id}/design

Claude turns the request into columns, distributions and relationships (design), re-syncs after you edit the request (rewrite), or applies one change (refine). Before the result is saved it looks up published rates (research, on by default), reads attached documents, has the design reviewed by a domain expert pass and calibrates it to cited targets. Runs in the background: returns 202 with a job id. A newer request replaces a design still running (refunded if it hadn't started); one sent while another is being queued gets 409.

Body
{ "task": "design", "request": "Support tickets for a B2B SaaS: priority, product area, CSAT that drops with wait time…", "research": true, "documents": [ … ] }
Returns
{ "jobId": "…", "status": "queued" }
GET/api/datasets/{id}/design?job={jobId}

The design's progress (status, step message) and, once it has succeeded, the reply, request, spec and what the pipeline found (facts, review, calibration).

Returns
{ "status": "succeeded", "reply": "…", "spec": { … }, "design": { "facts": [ … ], "review": { … }, "calibration": [ … ] } }
POST/api/datasets/{id}/documents

Attach a data dictionary, codebook, protocol or paper for the next design (multipart form field "file"; PDF, text, Markdown, CSV or JSON, up to 4 MB, at most 5). Pass the returned document in the design's documents list.

Returns
{ "document": { "key": "…", "filename": "codebook.pdf", "mediaType": "application/pdf", "bytes": 182044 } }
GET/api/datasets/{id}/design/report

The design report, measured on a fresh sample: each target vs achieved with its source, each relationship asked vs achieved, label learnability and leakage, and the expert review.

POST/api/jobs

Export it: pass the spec you want generated. Add whatIf to the spec for paired factual and counterfactual rows: each unit once as it is and once per scenario, with unit_id and scenario columns.

Body
{ "datasetId": "…", "format": "csv", "spec": { …, "whatIf": { "scenarios": [{ "name": "everyone treated", "set": [{ "column": "treatment", "value": "drug A" }] }] } } }

Options

What an export generates. Every field is optional except rows; omitted fields keep the dataset's saved settings.

{
  "rows": 100000,                         // rows to generate (main table for related tables)
  "seed": 1,                              // same seed + same model = same rows
  "classMix": { "\">50K\"": 0.5, "\"<=50K\"": 0.5 },   // rebalance the label (JSON-encoded values)
  "columnMix": { "column": "sex", "mix": { "\"Female\"": 0.5, "\"Male\"": 0.5 } },
  "focus": { "column": "state", "value": "CA" },        // only rows with this value
  "conditions": [{ "column": "age", "min": 65 }, { "column": "plan", "values": ["pro", "enterprise"] }],
  "extendTime": { "column": "visit_date", "months": 6 },   // the months after the data ends
  "nullRates": { "phone": 0.2 },
  "rules": { "disabled": ["order:start<=end"], "custom": [] },
  "flagColumn": true,                     // add is_synthetic = true
  "writeText": true,                      // Claude writes free-text columns
  "missingMarkers": true                  // CSV keeps the source's "?" / "N/A" placeholders
}