Appearance
AI Context File
Copy the entire block below and paste it into your AI assistant to give it full knowledge of the Determ API. This works with any AI tool -- ChatGPT, Claude, Cursor, GitHub Copilot, or any other LLM-based assistant.
How to use it:
- ChatGPT / Claude -- Start a new conversation, paste the block, then ask your question in the same message or in a follow-up.
- Cursor -- Paste the block into a
.cursorrulesfile in your project root for persistent context, or paste it into the chat for a one-off session. - Claude Code -- Paste the block into a
CLAUDE.mdfile in your project root, or include it directly in your prompt.
markdown
# Determ API -- Complete Reference
## Overview
Determ is a media monitoring platform. The public API lets you programmatically access mentions, create keywords, and retrieve metadata.
- **Base URL:** `https://api.mediatoolkit.com`
- **Authentication:** Bearer token in the `Authorization` header (`Authorization: Bearer YOUR_API_TOKEN`)
- **Response format:** JSON
- **Content-Type for POST requests:** `application/json`
---
## Endpoints
### GET /v2/me
Returns the authenticated user's profile, including organization memberships and roles.
**Headers:**
- `Authorization: Bearer YOUR_API_TOKEN` (required)
**Response fields:**
| Field | Type | Description |
|-------|------|-------------|
| `id` | int64 | User ID |
| `email` | string | Email address |
| `name` | string | Display name |
| `accessToken` | string | Current access token |
| `createdTime` | int64 | Account creation timestamp |
| `lastLoginTime` | int64 | Last login timestamp |
| `languageCode` | string | Preferred language code (e.g. `en`, `hr`) |
| `verified` | boolean | Whether the email is verified |
| `organizationIds` | array of int64 | Organization IDs the user belongs to |
| `organizationRoles` | object | Map of org ID to role object (`id`, `name`, `isAdmin`, `canUpdate`, `canInvite`, `canUpdateMentions`) |
**Example response:**
```json
{
"id": 12345,
"email": "user@example.com",
"name": "John Doe",
"languageCode": "en",
"verified": true,
"organizationIds": [100, 200],
"organizationRoles": {
"100": {
"id": "admin",
"name": "Admin",
"isAdmin": true,
"canUpdate": true,
"canInvite": true,
"canUpdateMentions": true
}
}
}
```
---
### GET /v2/languages
Returns all supported languages. Use the returned codes when configuring keyword filters.
**Response:**
```json
{
"languages": [
{ "code": "en", "name": "English" },
{ "code": "hr", "name": "Croatian" },
{ "code": "de", "name": "German" }
]
}
```
**Fields:** `languages[].code` (string, ISO language code), `languages[].name` (string, English name).
---
### GET /v2/locations
Returns all supported locations. Use the returned codes when configuring keyword filters.
**Response:**
```json
{
"locations": [
{ "code": "US", "name": "United States" },
{ "code": "HR", "name": "Croatia" },
{ "code": "GB", "name": "United Kingdom" }
]
}
```
**Fields:** `locations[].code` (string, ISO country code), `locations[].name` (string, English name).
---
### GET /v2/organizations/{organizationId}/tags
Returns tags for a given organization, filtered by the provided tag IDs.
**Path parameters:**
- `organizationId` (int64, required)
**Query parameters:**
- `ids` (string, required) -- Comma-separated list of tag IDs (int64 values)
**Response:**
```json
{
"tags": [
{ "id": 1, "name": "Important" },
{ "id": 2, "name": "Reviewed" }
]
}
```
**Fields:** `tags[].id` (int64), `tags[].name` (string).
---
### POST /v2/organizations/{organizationId}/keywords
Creates a new keyword (monitoring query) within the specified organization.
**Path parameters:**
- `organizationId` (int64, required)
**Request body:**
| Field | Type | Description |
|-------|------|-------------|
| `groupId` | int64 | ID of the group to add the keyword to (required) |
| `name` | string | Display name for the keyword (required) |
| `keyword` | object | Keyword definition object (required) |
| `keyword.naturalQuery` | string | Search query. Supports boolean operators (`OR`, `AND`, quoted phrases) |
| `keyword.mayLangs` | array of string | Language codes to include (e.g. `["en", "de"]`) |
| `keyword.mayLocations` | array of string | Location codes to include (e.g. `["US", "GB"]`) |
| `keyword.maySourceTypes` | array of string | Source types to monitor (see Source Types enum) |
**Example request:**
```json
{
"groupId": 456,
"name": "Tesla Mentions",
"keyword": {
"naturalQuery": "Tesla OR \"Elon Musk\"",
"mayLangs": ["en", "de"],
"maySourceTypes": ["WEB", "TWITTER", "FACEBOOK"]
}
}
```
**Response fields:**
| Field | Type | Description |
|-------|------|-------------|
| `id` | int64 | Created keyword ID |
| `name` | string | Display name |
| `color` | string | Hex color code |
| `naturalQuery` | string | The search query |
| `active` | boolean | Whether the keyword is actively monitoring |
| `createdAt` | string (ISO 8601) | Creation timestamp |
| `editedAt` | string (ISO 8601) | Last edit timestamp |
---
### POST /v2/organizations/{organizationId}/keywords/{keywordId}/mentions/count
Returns the total number of mentions for a specific keyword, broken down by source type.
**Path parameters:**
- `organizationId` (int64, required)
- `keywordId` (int32, required)
**Request body (FeedsSearchQueryRequest):**
```json
{
"query": {
"feeds": [
{ "groupId": 200, "keywordId": 5001 }
],
"publishedTime": {
"relative": "LAST_7_DAYS"
},
"mentionFilter": {
"mentionType": { "any": ["WEB", "TWITTER"] },
"sentiment": { "any": ["POSITIVE", "NEUTRAL"] }
}
}
}
```
**Query fields explained:**
- `query.feeds` -- Optional array of `{ groupId, keywordId }` to scope the search. Omit to search all.
- `query.publishedTime` -- Time range filter based on publication time. Use `relative` (enum) for preset ranges, or `from`/`to` (ISO 8601 datetime) for custom ranges.
- `query.feedTime` -- Same structure as `publishedTime`, based on when the mention entered the feed.
- `query.mentionFilter` -- Supports `mentionType` and `sentiment` filters only. Each uses a **FilterClause** pattern with `any`, `all`, `must`, and `not` sub-arrays.
**Response:**
```json
{
"count": 1523,
"typeToCount": {
"WEB": 890,
"TWITTER": 423,
"FACEBOOK": 210
}
}
```
**Fields:** `count` (int64, total), `typeToCount` (object, breakdown by source type).
---
### POST /v2/organizations/{organizationId}/groups/{groupId}/mentions/count
Same as the keyword-level count endpoint, but scoped to a single group.
**Path parameters:**
- `organizationId` (int64, required)
- `groupId` (int64, required)
**Request body:** Same FeedsSearchQueryRequest as above (mentionType and sentiment filters only).
**Response:** Same format -- `count` and `typeToCount`.
---
### POST /v2/organizations/{organizationId}/keywords/{keywordId}/mentions/scroll
Returns a paginated list of mentions with full mention data for a specific keyword.
**Path parameters:**
- `organizationId` (int64, required)
- `keywordId` (int32, required)
**Request body (ScrollRequest):**
```json
{
"query": {
"publishedTime": { "relative": "LAST_7_DAYS" },
"mentionFilter": {
"sentiment": { "any": ["POSITIVE"] }
}
},
"paged": {
"count": 20,
"sorted": {
"direction": "DESC",
"property": "PUBLISHED_TIME"
}
},
"scrollToken": null
}
```
**ScrollRequest fields:**
- `query` -- FeedsSearchQueryRequest (same schema as count endpoints -- mentionType and sentiment filters only)
- `paged.count` (int32) -- Mentions per page
- `paged.sorted.direction` -- `ASC` or `DESC`
- `paged.sorted.property` -- Sort field (see Sort Properties enum)
- `scrollToken` (string) -- Token from previous response for next page. Omit on first request.
**Response:**
```json
{
"mentions": [
{
"id": 987654321,
"type": "WEB",
"title": "Tesla announces new factory",
"mention": "Tesla has announced plans to build a new factory...",
"url": "https://example.com/article/tesla-factory",
"author": "TechNews",
"reach": 50000,
"virality": 0.85,
"interaction": 1200,
"influenceScore": 78,
"autoSentiment": "POSITIVE",
"languages": ["en"],
"locations": ["US"],
"insertTime": 1705312800000,
"keywords": ["Tesla"]
}
],
"scrollToken": "eyJzZWFyY2hBZnRlciI6..."
}
```
**Mention object fields:**
| Field | Type | Description |
|-------|------|-------------|
| `id` | int64 | Mention ID |
| `type` | string | Source type |
| `title` | string | Title (may be null) |
| `mention` | string | Text snippet |
| `url` | string | Original URL |
| `author` | string | Author name/handle |
| `influencer` | string | Influencer name (if any) |
| `reach` | int32 | Estimated audience reach |
| `virality` | double | Virality score |
| `interaction` | int64 | Total interactions |
| `influenceScore` | int64 | Influence score |
| `autoSentiment` | string | Detected sentiment |
| `languages` | array of string | Detected languages |
| `locations` | array of string | Detected locations |
| `insertTime` | int64 | Ingestion timestamp (epoch ms) |
| `databaseInsertTime` | int64 | DB storage timestamp (epoch ms) |
| `image` | string | Associated image URL (if any) |
| `keywords` | array of string | Matched keywords |
**Pagination flow:**
1. First request: Send query without `scrollToken`. Response includes results + `scrollToken`.
2. Next page: Include the `scrollToken` from previous response. Keep the same `query` and `paged` params.
3. End: `scrollToken` is `null` or absent when no more results remain.
---
### POST /v2/organizations/{organizationId}/groups/{groupId}/mentions/scroll
Same as the keyword-level scroll endpoint, but scoped to a single group.
**Path parameters:**
- `organizationId` (int64, required)
- `groupId` (int64, required)
**Request body:** Same ScrollRequest as above.
**Response:** Same format -- `mentions` array and `scrollToken`.
---
## Enums
### Source Types
`WEB`, `FACEBOOK`, `TWITTER`, `FORUM`, `COMMENT`, `YOUTUBE`, `INSTAGRAM`, `REDDIT`, `TIKTOK`, `PRINT`, `RADIO`, `TV`, `BLUESKY`
### Sentiments
`POSITIVE`, `NEUTRAL`, `NEGATIVE`, `UNDEFINED`
### Relative Time Periods
`TODAY`, `YESTERDAY`, `LAST_24_HOURS`, `LAST_7_DAYS`, `LAST_30_DAYS`, `LAST_90_DAYS`, `THIS_MONTH`, `THIS_WEEK`, `LAST_WEEK`, `LAST_MONTH`
### Sort Properties
`PUBLISHED_TIME`, `FEED_TIME`, `REACH`, `VIRALITY`, `INTERACTIONS`, `INFLUENCE`, `ENGAGEMENT`
### Sort Directions
`ASC`, `DESC`
---
## Filter Clause Pattern
All mention filters use the same structure:
```json
{
"any": ["VALUE_1", "VALUE_2"],
"all": ["VALUE_3"],
"must": ["VALUE_4"],
"not": ["VALUE_5"]
}
```
- `any` -- Match any of the listed values (OR logic)
- `all` -- Match all of the listed values (AND logic)
- `must` -- Must match these values
- `not` -- Exclude these values
Most common usage is `any` for inclusive filtering and `not` for exclusions.
---
## Error Handling
**Error response format:**
```json
{
"status": 400,
"message": "Invalid parameter: fromDate must be in ISO 8601 format",
"timestamp": 1713200000000
}
```
**HTTP status codes:**
| Status | Meaning | Retry? |
|--------|---------|--------|
| 400 | Bad Request -- malformed request | No, fix the request |
| 401 | Unauthorized -- missing/invalid token | No, check token |
| 403 | Forbidden -- valid token, no permission | No, check access |
| 404 | Not Found -- resource does not exist | No, check IDs |
| 429 | Too Many Requests -- rate limit exceeded | Yes, with backoff |
| 500 | Internal Server Error | Yes, with backoff |
| 502 | Bad Gateway | Yes, with backoff |
| 503 | Service Unavailable | Yes, with backoff |
**Retry logic:** For 429/5xx errors, use exponential backoff starting at 1 second, doubling each attempt, up to 3 retries. Do not retry 4xx errors (except 429).
---
## Rules & Conventions
1. **Always include the Authorization header** with `Bearer YOUR_API_TOKEN` on every request.
2. **Use `scrollToken` for pagination** -- do not increment page numbers manually. Pass the token from the previous response.
3. **Date formats:** Use ISO 8601 (`2024-01-15T00:00:00Z`) for specific times, or the relative enum (`LAST_7_DAYS`) for preset ranges.
4. **ID types:** All IDs are int64 except `keywordId` which is int32.
5. **Keep query params stable during pagination** -- only update `scrollToken` between scroll requests.
6. **Rate limiting:** Keep requests under 60/minute as a conservative default. Implement exponential backoff for 429 responses.
7. **Content-Type:** Always send `Content-Type: application/json` for POST requests.