Skip to main content

Base URL

All Query API endpoints are available at:
https://api.grainql.com/v1/api/query

Authentication

All Query API requests require authentication via the X-API-Key header:
X-API-Key: YOUR_SECRET_KEY

Creating an API Key

  1. Navigate to Dashboard → Settings → Authentication
  2. Click Generate New Secret
  3. Enter a descriptive name
  4. Check the “Query API” permission
  5. Copy the generated secret (it’s only shown once!)
Authentication Strategy: The Query API authentication is independent of your SDK authentication strategy. You can use Query API keys regardless of whether your SDK is set to NONE, SERVER_SIDE, or JWT authentication.
Store your API key securely in environment variables. Never commit it to version control or expose it in client-side code.

Endpoints

MethodEndpointDescription
POST/{tenantId}Query events with filters and pagination
POST/count/{tenantId}Get count of events matching filters
GET/events/{tenantId}List all distinct event names
Important: Use your tenant alias (shown on the dashboard) as the {tenantId} parameter, not the UUID. The alias is the human-readable identifier you see in your dashboard.

Rate Limits

Rate limits are enforced per API key and vary by plan:
PlanRequests per MinuteRequests per Day
FreeNot availableNot available
Builder220
Growth10200
Scale1002,000
Need Higher Limits? These rate limits are designed for typical analytics use cases. For high-frequency applications, real-time dashboards, or bulk data exports, we offer custom plans with higher limits. Contact us at [email protected] to discuss your requirements.

Rate Limit Headers

When you approach your rate limit, responses include helpful headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1640995200
When you exceed your rate limit, you’ll receive a 429 Too Many Requests response with a Retry-After header:
HTTP/1.1 429 Too Many Requests
Retry-After: 60

Error Handling

The Query API uses standard HTTP status codes and returns JSON error responses:

401 Unauthorized

Missing or invalid API key:
{
  "error": "Missing X-API-Key header"
}
{
  "error": "Invalid API key or insufficient permissions"
}

403 Forbidden

Insufficient plan tier:
{
  "error": "Query API requires Builder plan or higher. Upgrade at https://grainql.com/settings/billing"
}

429 Too Many Requests

Rate limit exceeded:
{
  "error": "Rate limit exceeded. Upgrade for higher limits."
}

400 Bad Request

Invalid request parameters:
{
  "error": "Invalid date format. Use YYYY-MM-DD format."
}

404 Not Found

Tenant not found:
{
  "error": "Tenant not found"
}

Request/Response Format

Content Type

All requests must include:
Content-Type: application/json

Date Format

Dates should be in ISO 8601 format (YYYY-MM-DD):
{
  "after": "2024-01-01",
  "before": "2024-01-31"
}

Pagination

Use pagination to handle large result sets:
{
  "pagination": {
    "offset": 0,
    "size": 100
  }
}
  • offset: Number of records to skip (0-based)
  • size: Maximum number of records to return (max: 1000)

Response Format

Query responses return an array of event objects:
[
  {
    "eventName": "page_viewed",
    "userId": "user_123",
    "eventTs": "2024-01-15T10:30:00Z",
    "properties": {
      "page": "/home",
      "referrer": "https://google.com"
    },
    "eventDate": "2024-01-15",
    "insertId": "unique-event-id"
  }
]
Count responses return a simple object:
{
  "count": 15247
}

Common Patterns

Basic Query

curl -X POST https://api.grainql.com/v1/api/query/your-tenant-id \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_SECRET_KEY" \
  -d '{
    "event": "page_viewed",
    "after": "2024-01-01",
    "before": "2024-01-31",
    "pagination": { "offset": 0, "size": 100 }
  }'

Filtered Query

curl -X POST https://api.grainql.com/v1/api/query/your-tenant-id \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_SECRET_KEY" \
  -d '{
    "event": "purchase_completed",
    "filterSet": [
      {
        "property": "properties.price",
        "comparison": "GREATER_THAN",
        "value": 100
      }
    ],
    "pagination": { "offset": 0, "size": 50 }
  }'

Count Query

curl -X POST https://api.grainql.com/v1/api/query/count/your-tenant-id \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_SECRET_KEY" \
  -d '{
    "event": "button_clicked",
    "after": "2024-01-01",
    "before": "2024-01-31"
  }'

List Events

curl https://api.grainql.com/v1/api/query/events/your-tenant-id \
  -H "X-API-Key: YOUR_SECRET_KEY"

SDK Integration

While the Query API works with any HTTP client, you can also use it with the Grain Analytics SDK for a more integrated experience:
import { createGrainAnalytics } from '@grainql/analytics-web';

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'SERVER_SIDE',
  secretKey: process.env.GRAIN_API_KEY
});

// The SDK doesn't include Query API methods yet,
// but you can use fetch alongside it
const response = await fetch('https://api.grainql.com/v1/api/query/your-tenant-id', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.GRAIN_API_KEY
  },
  body: JSON.stringify({
    event: 'page_viewed',
    pagination: { offset: 0, size: 100 }
  })
});

const events = await response.json();

Custom Plans

Need higher rate limits or have specific usage requirements? We offer custom plans tailored to your needs:
  • Higher rate limits: Custom requests per minute/day limits
  • Dedicated support: Priority support and SLA guarantees
  • Custom features: Tailored analytics features and integrations
  • Volume discounts: Competitive pricing for high-volume usage
Contact us at [email protected] to discuss your requirements and get a custom quote.

Next Steps