JSON Tables
JSON Tables is a premium hosted data backend for customers who outgrow Google Sheets but don’t need the complexity of Airtable or a full database. It provides the same familiar JSON API with 10x better performance, no rate limits, and higher row limits.
Why JSON Tables?
The Problem with Google Sheets at Scale
As your application grows, Google Sheets starts to show its limitations:
| Challenge | Google Sheets | JSON Tables |
|---|---|---|
| Response time | 2-5 seconds | ~50ms |
| Row limit | ~50K (usable) | 500K+ |
| Rate limits | Google’s quotas (429 errors) | Plan-based, no Google limits |
| Concurrent access | Limited | Unlimited |
| Formula complexity | Slows everything | N/A (pure data) |
Same API, Zero Code Changes
The best part? JSON Tables uses the exact same API format as your Google Sheets integration. Migrating is as simple as changing your endpoint URL:
// Before (Google Sheets)
const response = await fetch('https://sheetsjson.com/api/sheets/my-account/products');
// After (JSON Tables)
const response = await fetch('https://sheetsjson.com/api/tables/my-account/products');
That’s it. Your existing code continues to work.
Getting Started
Creating a Table
- Navigate to Tables in your dashboard
- Click Create Table
- Enter a name and optional description
- Your table is ready!
Your First API Request
Once you create a table, you’ll get an API endpoint like:
GET /api/tables/your-account/products
The response format is identical to Google Sheets:
{
"data": [
{"_row": 1, "id": "uuid-here", "name": "Widget", "price": 29.99},
{"_row": 2, "id": "uuid-here", "name": "Gadget", "price": 49.99}
],
"meta": {
"total": 2,
"limit": 100,
"offset": 0,
"table_name": "products"
}
}
API Reference
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET |
/api/tables/:account/:table |
List rows (paginated) |
| GET |
/api/tables/:account/:table/:id |
Get single row by ID |
| POST |
/api/tables/:account/:table |
Create row(s) |
| PUT |
/api/tables/:account/:table/:id |
Update a row |
| DELETE |
/api/tables/:account/:table/:id |
Delete a row |
Query Parameters
All the familiar query parameters work:
Pagination
GET /api/tables/my-account/products?limit=25&offset=50
-
limit- Maximum rows to return (default: 100, max: 1000) -
offset- Number of rows to skip
Sorting
GET /api/tables/my-account/products?sort=price:desc
-
sort- Field name with direction (ascordesc) - Supports sorting by any field in your data
Filtering
GET /api/tables/my-account/products?filter[category]=electronics&filter[available]=true
-
filter[field]=value- Filter by exact match - Multiple filters are combined with AND
Field Selection
GET /api/tables/my-account/products?fields=id,name,price
-
fields- Comma-separated list of fields to include - Reduces payload size
Creating Rows
Single Row
curl -X POST https://sheetsjson.com/api/tables/my-account/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer st_your_api_key" \
-d '{"name": "New Product", "price": 39.99, "category": "electronics"}'
Response:
{
"data": [
{"_row": 3, "id": "uuid-here", "name": "New Product", "price": 39.99, "category": "electronics"}
],
"meta": {
"created": 1
}
}
Bulk Insert
curl -X POST https://sheetsjson.com/api/tables/my-account/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer st_your_api_key" \
-d '[
{"name": "Product A", "price": 19.99},
{"name": "Product B", "price": 29.99},
{"name": "Product C", "price": 39.99}
]'
Response:
{
"data": [
{"_row": 4, "id": "uuid-1", "name": "Product A", "price": 19.99},
{"_row": 5, "id": "uuid-2", "name": "Product B", "price": 29.99},
{"_row": 6, "id": "uuid-3", "name": "Product C", "price": 39.99}
],
"meta": {
"created": 3
}
}
Updating Rows
You can update by row ID (UUID) or row number:
# By UUID
curl -X PUT https://sheetsjson.com/api/tables/my-account/products/uuid-here \
-H "Content-Type: application/json" \
-H "Authorization: Bearer st_your_api_key" \
-d '{"price": 34.99}'
# By row number
curl -X PUT https://sheetsjson.com/api/tables/my-account/products/3 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer st_your_api_key" \
-d '{"price": 34.99}'
Deleting Rows
curl -X DELETE https://sheetsjson.com/api/tables/my-account/products/uuid-here \
-H "Authorization: Bearer st_your_api_key"
Returns 204 No Content on success.
Authentication
Tables support the same authentication methods as Sheets:
API Key (Optional)
If you enable “Require API Key” for your table:
# Header (recommended)
curl -H "Authorization: Bearer st_your_api_key" \
https://sheetsjson.com/api/tables/my-account/products
# X-API-Key header
curl -H "X-API-Key: st_your_api_key" \
https://sheetsjson.com/api/tables/my-account/products
# Query parameter (not recommended for production)
curl "https://sheetsjson.com/api/tables/my-account/products?api_key=st_your_api_key"
Public Access
If API key is not required, your table is publicly readable (writes always require authentication).
Table Settings
Custom API Slug
Instead of the auto-generated slug, set a memorable one:
/api/tables/my-account/my-custom-slug
Cache TTL
Configure how long responses are cached (default: 60 seconds). Lower values mean fresher data; higher values mean faster responses.
API Key Regeneration
If your API key is compromised, regenerate it in the table settings. The old key stops working immediately.
Performance Comparison
We benchmarked a table with 10,000 rows:
| Operation | Google Sheets | JSON Tables | Improvement |
|---|---|---|---|
| Read (p50) | 2,100ms | 48ms | 44x faster |
| Read (p99) | 4,800ms | 185ms | 26x faster |
| Write | 1,200ms | 92ms | 13x faster |
| Filtered query | 3,500ms | 65ms | 54x faster |
Migrating from Google Sheets
Option 1: Fresh Start
- Create a new JSON Table
-
Update your API endpoint URL (change
/sheets/to/tables/) - Use the POST endpoint to populate data
- Done!
Option 2: Import from Sheet (Coming Soon)
We’re building a one-click import wizard that will:
- Read your existing Google Sheet
- Auto-detect column types
- Import all rows
- Generate an equivalent API endpoint
Best Practices
Schema Design
Unlike Google Sheets, JSON Tables don’t have column headers defined by a row. Define your columns in the table settings for:
- Type validation
- Documentation
- Better error messages
Indexing for Filters
Filtering is fast thanks to PostgreSQL GIN indexes, but for best performance:
- Use consistent field names (case-sensitive)
- Avoid deeply nested objects in filters
- Use exact matches when possible
Caching Strategy
Set cache TTL based on your data freshness needs:
- Real-time data (stock prices): 0-10 seconds
- Frequently updated (inventory): 30-60 seconds
- Mostly static (product catalog): 300+ seconds
Pricing & Limits
JSON Tables are available on Pro plans and above:
| Plan | Tables | Rows per Table | Price |
|---|---|---|---|
| Free | 0 | — | $0/mo |
| Starter | 0 | — | $9.99/mo |
| Pro | 3 | 100,000 | $29/mo |
| Business | 10 | 500,000 | $99/mo |
| Enterprise | Unlimited | Custom | Contact us |
Error Codes
| Code | Meaning |
|---|---|
| 400 | Bad request (invalid JSON or parameters) |
| 401 | API key required or invalid |
| 403 | Write permission denied (upgrade needed) or row limit exceeded |
| 404 | Table or row not found |
| 429 | Rate limit exceeded |
| 503 | Table paused or in error state |
FAQ
Can I use both Sheets and Tables?
Absolutely! Many customers use Sheets for content that non-technical team members edit, and Tables for high-traffic data that needs better performance.
What happens to my data?
Your data is stored in PostgreSQL with automatic backups. We never delete your data unless you explicitly request it.
Is there a way to edit data in a spreadsheet UI?
The Tables dashboard provides a basic data grid for viewing and editing rows. For bulk edits, you can export to CSV, edit in your favorite spreadsheet app, and re-import.
Can I migrate back to Google Sheets?
Yes! Export your table data as CSV anytime and import it into Google Sheets.
Next Steps
Questions? Contact our support team.