TinyTables API
Access your table data programmatically using the REST API. Build custom integrations, sync data with external systems, or create frontends powered by your TinyTable data.
Authentication
All API requests require an API key in the Authorization header:
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.tinycommand.com/v1/tables
Generate API keys in Settings → API Keys (see API Keys).
Base URL
https://api.tinycommand.com/v1
Endpoints
List tables
GET /tables
Returns all tables in your workspace.
Response:
{
"tables": [
{
"id": "tbl_abc123",
"name": "Leads",
"columns": 12,
"rows": 1547,
"created_at": "2024-01-15T10:00:00Z"
}
]
}
Get table schema
GET /tables/:tableId
Returns the table's column definitions.
Response:
{
"id": "tbl_abc123",
"name": "Leads",
"columns": [
{
"id": "col_name",
"name": "Name",
"type": "text",
"required": true
},
{
"id": "col_status",
"name": "Status",
"type": "choice",
"options": ["New", "Contacted", "Qualified", "Won", "Lost"]
}
]
}
List rows
GET /tables/:tableId/rows
Query parameters:
| Parameter | Type | Description |
|---|---|---|
limit | Number | Max rows to return (default: 100, max: 1000) |
offset | Number | Skip N rows for pagination |
sort | String | Column to sort by (e.g., created_at) |
order | String | asc or desc |
filter | JSON | Filter conditions (URL-encoded) |
Filter examples:
# Rows where status equals "New"
?filter={"status":{"eq":"New"}}
# Rows where score is greater than 80
?filter={"score":{"gt":80}}
# Multiple conditions (AND)
?filter={"status":{"eq":"New"},"score":{"gt":80}}
Response:
{
"rows": [
{
"id": "row_xyz789",
"fields": {
"Name": "Sarah Chen",
"Email": "sarah@acme.com",
"Status": "New",
"Score": 92
},
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-15T14:22:00Z"
}
],
"total": 1547,
"limit": 100,
"offset": 0
}
Create a row
POST /tables/:tableId/rows
Content-Type: application/json
Body:
{
"fields": {
"Name": "New Lead",
"Email": "lead@example.com",
"Status": "New",
"Score": 75
}
}
Response: The created row with its generated id.
Update a row
PATCH /tables/:tableId/rows/:rowId
Content-Type: application/json
Body: Only include fields you want to update:
{
"fields": {
"Status": "Contacted",
"Score": 85
}
}
Delete a row
DELETE /tables/:tableId/rows/:rowId
Returns 204 No Content on success.
Bulk operations
Create multiple rows
POST /tables/:tableId/rows/bulk
Content-Type: application/json
Body:
{
"rows": [
{ "fields": { "Name": "Lead 1", "Email": "a@b.com" } },
{ "fields": { "Name": "Lead 2", "Email": "c@d.com" } }
]
}
Maximum 100 rows per request.
Delete multiple rows
DELETE /tables/:tableId/rows/bulk
Content-Type: application/json
Body:
{
"row_ids": ["row_abc", "row_def", "row_ghi"]
}
Rate limits
| Plan | Requests per minute |
|---|---|
| Free | 100 |
| Starter | 500 |
| Pro | 1,000 |
| Enterprise | Custom |
Rate limit headers are included in every response:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1710500000
Error handling
| Status code | Meaning |
|---|---|
400 | Bad request. Check your request body or parameters |
401 | Unauthorized. Invalid or missing API key |
403 | Forbidden. API key doesn't have permission for this table |
404 | Not found. Table or row doesn't exist |
429 | Rate limit exceeded. Slow down |
500 | Server error. Retry after a moment |
Error response format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Field 'Status' must be one of: New, Contacted, Qualified, Won, Lost",
"field": "Status"
}
}
Use pagination for large tables. Fetching all rows at once (limit: 1000) is slower than paginating through 100 rows at a time. Always use offset + limit for production integrations.
API keys have full read/write access by default. Use scoped keys (table-specific, read-only) in production to limit the blast radius if a key is leaked.