> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grainql.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Query API Overview

> Complete reference for the Grain Query API endpoints

## 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:

```bash theme={null}
X-API-Key: YOUR_SECRET_KEY
```

### Creating an API Key

1. Navigate to [Dashboard → Settings → Authentication](https://grainql.com/dashboard/settings)
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!)

<Note>
  **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.
</Note>

<Warning>
  Store your API key securely in environment variables. Never commit it to version control or expose it in client-side code.
</Warning>

## Endpoints

| Method | Endpoint             | Description                              |
| ------ | -------------------- | ---------------------------------------- |
| `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            |

<Note>
  **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.
</Note>

## Rate Limits

Rate limits are enforced per API key and vary by plan:

| Plan    | Requests per Minute | Requests per Day |
| ------- | ------------------- | ---------------- |
| Free    | Not available       | Not available    |
| Builder | 2                   | 20               |
| Growth  | 10                  | 200              |
| Scale   | 100                 | 2,000            |

<Note>
  **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](mailto:support@grainql.com) to discuss your requirements.
</Note>

### 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:

```json theme={null}
{
  "error": "Missing X-API-Key header"
}
```

```json theme={null}
{
  "error": "Invalid API key or insufficient permissions"
}
```

### 403 Forbidden

Insufficient plan tier:

```json theme={null}
{
  "error": "Query API requires Builder plan or higher. Upgrade at https://grainql.com/settings/billing"
}
```

### 429 Too Many Requests

Rate limit exceeded:

```json theme={null}
{
  "error": "Rate limit exceeded. Upgrade for higher limits."
}
```

### 400 Bad Request

Invalid request parameters:

```json theme={null}
{
  "error": "Invalid date format. Use YYYY-MM-DD format."
}
```

### 404 Not Found

Tenant not found:

```json theme={null}
{
  "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`):

```json theme={null}
{
  "after": "2024-01-01",
  "before": "2024-01-31"
}
```

### Pagination

Use pagination to handle large result sets:

```json theme={null}
{
  "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:

```json theme={null}
[
  {
    "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:

```json theme={null}
{
  "count": 15247
}
```

## Common Patterns

### Basic Query

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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

```bash theme={null}
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:

```typescript theme={null}
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](mailto:support@grainql.com) to discuss your requirements and get a custom quote.

## Next Steps

<CardGroup cols={2}>
  <Card title="Query Events" icon="search" href="/api-reference/query-api/query-events">
    Learn how to query events with filters
  </Card>

  <Card title="Count Events" icon="hash" href="/api-reference/query-api/count-events">
    Get event counts for aggregations
  </Card>

  <Card title="List Events" icon="list" href="/api-reference/query-api/list-events">
    Discover available event types
  </Card>

  <Card title="Filter Reference" icon="filter" href="/api-reference/query-api/filters">
    Complete filter operators guide
  </Card>
</CardGroup>
