Skip to main content

Base URL

All Query API endpoints are available at:
https://queryapis.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 support@grainql.com 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://queryapis.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://queryapis.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://queryapis.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://queryapis.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://queryapis.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 support@grainql.com to discuss your requirements and get a custom quote.

Next Steps

Query Events

Learn how to query events with filters

Count Events

Get event counts for aggregations

List Events

Discover available event types

Filter Reference

Complete filter operators guide