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:

ParameterTypeDescription
limitNumberMax rows to return (default: 100, max: 1000)
offsetNumberSkip N rows for pagination
sortStringColumn to sort by (e.g., created_at)
orderStringasc or desc
filterJSONFilter 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

PlanRequests per minute
Free100
Starter500
Pro1,000
EnterpriseCustom

Rate limit headers are included in every response:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1710500000

Error handling

Status codeMeaning
400Bad request. Check your request body or parameters
401Unauthorized. Invalid or missing API key
403Forbidden. API key doesn't have permission for this table
404Not found. Table or row doesn't exist
429Rate limit exceeded. Slow down
500Server 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"
  }
}
Tip

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.

Warning

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.