Dataville Connected sheets API
Connect a Google Sheet and query it alongside the public datasets.
Connect a Google Sheet and query it alongside the public datasets.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/dataville-com:dataville-com-connected-sheets-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Dataville Connected sheets API
version: 1.0.0
description: 'Dataville is a unified data API: a single REST surface over Wikipedia, arXiv,
Project Gutenberg, the US Census, USDA FoodData, Papers with Code, SEC EDGAR,
OpenAlex, PyPI and Stack Exchange — plus bulk CSV/Parquet/DuckDB export, a
SQL endpoint, and "bring your own data" Google Sheets connections.
## Authentication
Data, export and query endpoints never hard-reject at the middleware level — an
unauthenticated caller simply gets the anonymous tier (50 requests per day per
IP).
### API key
```
Authorization: Bearer dataville_xxxxxxxxxxxxxxxx
```
Create and manage keys from the **API Keys** page in the
Dataville dashboard. Keys start with `dataville_`
(legacy keys start with `datax_`). For clients that cannot set headers — DuckDB
WASM, the DuckDB CLI''s `httpfs` reader — the same key may be passed as the
`?token=` query parameter.
The Dataville web app authenticates with its own session tokens; those routes
are internal and not part of the public API surface.
## Credits and metering
Authenticated usage is metered against a monthly **Dataville credit** balance in
USD, shared across an account''s sessions and API keys. Each tier is granted
credit every month — Free **$5**, Developer **$29**, Pro **$99** — and each
query deducts a per-source, per-tier amount:
| Class | Free | Developer | Pro |
| --- | --- | --- | --- |
| Open/public datasets | $0.0050 | $0.0050 | $0.0040 |
| Wikipedia (premium) | $0.0250 | $0.0150 | $0.0125 |
A `POST /api/v1/query` call is charged per row returned. When the balance
reaches zero the API returns **402 Payment Required** until the monthly reset,
a plan upgrade, or a credit top-off. Every metered response carries:
- `X-Credits-Remaining` — remaining balance, USD
- `X-Credit-Cost` — amount charged for this request, USD
Check your request quota any time via `GET /api/v1/auth/usage`; your credit
balance is reported on every metered response in `X-Credits-Remaining` and on
the dashboard''s **Billing** page.
## Rate limiting
Anonymous callers are limited by IP (50 requests per 24 hours) and receive
**429** when they exceed it, with `X-RateLimit-Limit`, `X-RateLimit-Remaining`
and `X-RateLimit-Reset` headers. Authenticated callers are governed by credits
rather than a request count. Export and SQL routes carry their own stricter
limit (1,000 per day per account), as does support submission (5 per hour).
Every route also shares a burst limit of 100 requests per 15 minutes per IP,
checked before authentication. It answers **429** with `data.code:
RATE_LIMITED`, `RateLimit-*` headers and `Retry-After`. The anonymous daily
limit answers with `data.code: REQUEST_LIMIT_EXCEEDED`. Branch on the code:
the first clears in minutes; the second needs an API key or the daily reset.
## Errors
Every error body carries a human-readable `error`/`message` string that is safe
to show a user verbatim, and data-source errors additionally carry a stable
`data.code` (`UPSTREAM_RATE_LIMITED`, `UPSTREAM_UNAVAILABLE`,
`UPSTREAM_FORBIDDEN`, `UPSTREAM_BAD_RESPONSE`, `SOURCE_UNAVAILABLE`,
`INTERNAL_ERROR`) that clients should branch on instead of parsing prose. When
an upstream tells us how long to wait, the response sets `Retry-After`.
Rate-limit 429s carry a code too (`RATE_LIMITED`, `REQUEST_LIMIT_EXCEEDED`;
see Rate limiting).
## Stale results
A live source that is down is answered from Dataville''s own stored copy where
one exists. Those responses include a `data.stale` object naming the source and
when the copy was taken — the answer is real, but may be out of date.'
contact:
name: Dataville Support
email: support@dataville.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.dataville.com
description: Production
security:
- ApiKeyAuth: []
tags:
- name: Connected sheets
description: Connect a Google Sheet and query it alongside the public datasets.
paths:
/api/v1/datasets/{id}/query:
post:
tags:
- Connected sheets
operationId: queryDataset
summary: Run SQL against one connected sheet
description: 'A SELECT-only sandbox scoped to a single dataset. Reference the sheet as
`sheet_data` in the SQL. Accepts an API key as well as a Clerk session, so
connected data is queryable programmatically.
```bash
curl -X POST https://api.dataville.com/api/v1/datasets/my-budget-2026/query \
-H "Authorization: Bearer $DATAVILLE_API_KEY" \
-H "Content-Type: application/json" \
-d ''{"sql":"SELECT category, SUM(amount) FROM sheet_data GROUP BY 1"}''
```'
parameters:
- $ref: '#/components/parameters/DatasetId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- sql
properties:
sql:
type: string
maxLength: 10000
example: SELECT * FROM sheet_data LIMIT 10
responses:
'200':
description: Query results.
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: success
row_count:
type: integer
rows:
type: array
items:
type: object
additionalProperties: true
sql_executed:
type: string
'400':
description: Invalid SQL, or the dataset is not in a queryable state.
content:
application/json:
schema:
$ref: '#/components/schemas/SimpleError'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/DatasetNotFound'
'408':
description: Query exceeded the 30-second limit.
content:
application/json:
schema:
$ref: '#/components/schemas/SimpleError'
/api/v1/datasets/{id}/search:
get:
tags:
- Connected sheets
operationId: searchDataset
summary: Keyword-search a connected sheet
description: Search every column of a connected sheet without writing SQL. Accepts an API key.
parameters:
- $ref: '#/components/parameters/DatasetId'
- name: q
in: query
required: true
description: Search text (max 500 characters).
schema:
type: string
maxLength: 500
- name: limit
in: query
schema:
type: integer
default: 50
responses:
'200':
description: Matching rows.
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: success
row_count:
type: integer
rows:
type: array
items:
type: object
additionalProperties: true
'400':
description: '`q` is missing or too long, or the dataset is not ready.'
content:
application/json:
schema:
$ref: '#/components/schemas/SimpleError'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/DatasetNotFound'
components:
parameters:
DatasetId:
name: id
in: path
required: true
description: The dataset's UUID or its friendly slug (e.g. `my-budget-2026`).
schema:
type: string
schemas:
SimpleError:
type: object
properties:
status:
type: string
enum:
- error
message:
type: string
AuthError:
type: object
properties:
error:
type: string
message:
type: string
responses:
DatasetNotFound:
description: No such dataset for this account.
content:
application/json:
schema:
$ref: '#/components/schemas/SimpleError'
example:
status: error
message: Dataset not found.
Unauthorized:
description: Authentication required, or the token is invalid.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthError'
example:
error: Unauthorized
message: No token provided
securitySchemes:
ApiKeyAuth:
type: http
scheme: bearer
description: 'A Dataville API key, sent as `Authorization: Bearer dataville_...`. Clients
that cannot set headers may pass the same key as `?token=`.
'