API Reference

Complete REST API reference for the Scrya in-game advertising platform. All endpoints use JSON request/response bodies and require authentication.

Base URL

https://api.scrya.com/api/ads

Authentication

Every request must include a Bearer token in the Authorization header. Obtain your API key from the Scrya dashboard.

Authorization: Bearer YOUR_API_KEY

Requests without a valid token receive 401 Unauthorized.

Response Format

All responses are JSON. Successful mutations return the created/updated object. List endpoints return arrays. Errors return:

{
  "detail": "Human-readable error message"
}

Common status codes: 200 success, 201 created, 204 no content, 400 bad request, 401 unauthorized, 404 not found, 422 validation error, 429 rate limited.

Endpoints

Advertiser Management

Register as an advertiser, manage your profile, and deposit funds for ad spend.

Register as Advertiser

POST /register

Create a new advertiser account. Returns the created profile with your advertiser ID.

Request body:

  • company_name (string, required) — Your company or brand name
  • contact_email (string, required) — Primary contact email
  • website (string, optional) — Company website URL
curl -X POST https://api.scrya.com/api/ads/register \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "company_name": "Acme Corp",
    "contact_email": "[email protected]",
    "website": "https://acme.com"
  }'

Response (201):

{
  "id": "adv_abc123",
  "company_name": "Acme Corp",
  "contact_email": "[email protected]",
  "website": "https://acme.com",
  "balance_cents": 0,
  "created_at": "2026-03-01T12:00:00Z"
}

Get Profile

GET /profile

Retrieve the authenticated advertiser's profile, including current balance.

curl https://api.scrya.com/api/ads/profile \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "id": "adv_abc123",
  "company_name": "Acme Corp",
  "contact_email": "[email protected]",
  "website": "https://acme.com",
  "balance_cents": 500000,
  "created_at": "2026-03-01T12:00:00Z"
}

Update Profile

PATCH /profile

Update advertiser profile fields. Only include fields you want to change.

Request body (all optional):

  • company_name (string) — Updated company name
  • contact_email (string) — Updated email
  • website (string) — Updated website URL
curl -X PATCH https://api.scrya.com/api/ads/profile \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "company_name": "Acme Corporation"
  }'

Response (200): Returns the updated profile object.

Deposit Ad Spend

POST /deposit

Add funds to your advertiser balance. Funds are consumed when impressions are served.

Request body:

  • amount_cents (integer, required) — Amount to deposit in cents (e.g., 100000 = $1,000.00)
curl -X POST https://api.scrya.com/api/ads/deposit \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_cents": 100000
  }'

Response (200):

{
  "balance_cents": 600000,
  "deposited_cents": 100000
}

Campaigns

Create and manage advertising campaigns. Campaigns hold budget, targeting rules, and creatives.

Create Campaign

POST /campaigns

Create a new campaign. Campaigns start in draft status and must be activated separately.

Request body:

  • name (string, required) — Campaign name
  • budget_cents (integer, required) — Total budget in cents
  • cpm_bid_cents (integer, required) — CPM bid in cents. Range: 100-10000 ($1.00-$100.00 CPM)
  • daily_cap_cents (integer, optional) — Maximum daily spend in cents
  • start_date (string, optional) — ISO 8601 start date
  • end_date (string, optional) — ISO 8601 end date
  • priority (integer, optional) — Priority level 0-10 (higher = more likely to win ties). Default: 5
  • target_categories (array of strings, optional) — Game categories to target (e.g., ["simulation", "strategy"])
  • target_tags (array of strings, optional) — Content tags to target (e.g., ["political", "military"])
curl -X POST https://api.scrya.com/api/ads/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer 2026 Launch",
    "budget_cents": 500000,
    "cpm_bid_cents": 250,
    "daily_cap_cents": 25000,
    "start_date": "2026-06-01T00:00:00Z",
    "end_date": "2026-08-31T23:59:59Z",
    "priority": 7,
    "target_categories": ["simulation", "strategy"],
    "target_tags": ["political"]
  }'

Response (201):

{
  "id": "camp_abc123",
  "name": "Summer 2026 Launch",
  "status": "draft",
  "budget_cents": 500000,
  "spent_cents": 0,
  "cpm_bid_cents": 250,
  "daily_cap_cents": 25000,
  "start_date": "2026-06-01T00:00:00Z",
  "end_date": "2026-08-31T23:59:59Z",
  "priority": 7,
  "target_categories": ["simulation", "strategy"],
  "target_tags": ["political"],
  "created_at": "2026-03-01T12:00:00Z"
}

List Campaigns

GET /campaigns

List all campaigns for the authenticated advertiser. Returns an array sorted by creation date (newest first).

curl https://api.scrya.com/api/ads/campaigns \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Array of campaign objects.

Get Campaign Detail

GET /campaigns/{id}

Get full details for a single campaign, including spend totals and status.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Full campaign object.

Update Campaign

PATCH /campaigns/{id}

Update campaign fields. Only include fields you want to change. Cannot change budget below current spend.

Request body (all optional):

  • name (string)
  • budget_cents (integer)
  • cpm_bid_cents (integer, 100-10000)
  • daily_cap_cents (integer)
  • start_date (string)
  • end_date (string)
  • priority (integer, 0-10)
  • target_categories (array of strings)
  • target_tags (array of strings)
curl -X PATCH https://api.scrya.com/api/ads/campaigns/camp_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "budget_cents": 750000,
    "cpm_bid_cents": 300
  }'

Response (200): Updated campaign object.

Activate Campaign

POST /campaigns/{id}/activate

Activate a draft or paused campaign. The campaign must have at least one creative and sufficient advertiser balance. Activated campaigns begin serving immediately (or at start_date if set).

curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/activate \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "id": "camp_abc123",
  "status": "active",
  ...
}

Pause Campaign

POST /campaigns/{id}/pause

Pause an active campaign. Stops all ad serving immediately. Can be reactivated later.

curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/pause \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Campaign object with "status": "paused".

Geo Targeting

Add geographic targeting rules to campaigns. Supports country, state, city, and radius-based targeting. Multiple rules combine with OR logic (ad serves if any rule matches). Use include: false to exclude regions.

Add Geo Target

POST /campaigns/{id}/geo-targets

Request body:

  • target_type (string, required) — One of: country, state, city, radius
  • country_code (string, required for country/state/city) — ISO 3166-1 alpha-2 code (e.g., "US")
  • state_code (string, required for state) — State/province code (e.g., "CA")
  • city_name (string, required for city) — City name
  • lat (number, required for radius) — Latitude of center point
  • lng (number, required for radius) — Longitude of center point
  • radius_km (number, required for radius) — Radius in kilometers
  • include (boolean, optional)true to include (default), false to exclude
# Target the entire US
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/geo-targets \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target_type": "country",
    "country_code": "US",
    "include": true
  }'

# Target a 50km radius around San Francisco
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/geo-targets \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target_type": "radius",
    "lat": 37.7749,
    "lng": -122.4194,
    "radius_km": 50,
    "include": true
  }'

Response (201):

{
  "id": "geo_xyz789",
  "campaign_id": "camp_abc123",
  "target_type": "country",
  "country_code": "US",
  "include": true,
  "created_at": "2026-03-01T12:00:00Z"
}

List Geo Targets

GET /campaigns/{id}/geo-targets

List all geographic targeting rules for a campaign.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/geo-targets \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Array of geo target objects.

Remove Geo Target

DELETE /campaigns/{id}/geo-targets/{tid}

Remove a geographic targeting rule.

curl -X DELETE https://api.scrya.com/api/ads/campaigns/camp_abc123/geo-targets/geo_xyz789 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (204): No content.

Blocklist

Block your ads from appearing alongside specific content. Blocklist rules prevent matching when the game context contains the blocked value.

Add Blocklist Rule

POST /campaigns/{id}/blocklist

Request body:

  • rule_type (string, required) — One of: keyword, theme, category, action, game_id
  • value (string, required) — The value to block
# Block ads from appearing in violent contexts
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/blocklist \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rule_type": "keyword",
    "value": "violence"
  }'

# Block a specific game
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/blocklist \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rule_type": "game_id",
    "value": "game_xyz"
  }'

Response (201):

{
  "id": "blk_abc789",
  "campaign_id": "camp_abc123",
  "rule_type": "keyword",
  "value": "violence",
  "created_at": "2026-03-01T12:00:00Z"
}

List Blocklist Rules

GET /campaigns/{id}/blocklist

List all blocklist rules for a campaign.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/blocklist \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Array of blocklist rule objects.

Remove Blocklist Rule

DELETE /campaigns/{id}/blocklist/{rid}

Remove a blocklist rule.

curl -X DELETE https://api.scrya.com/api/ads/campaigns/camp_abc123/blocklist/blk_abc789 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (204): No content.

Creatives

Creatives are the ad content that gets woven into game narratives. Each creative has a type that determines how it integrates: narrative hooks appear as dialogue or narration, product mentions reference your brand naturally, location brands influence settings, and character references tie to NPCs.

Create Creative

POST /campaigns/{id}/creatives

Request body:

  • creative_type (string, required) — One of: narrative_hook, product_mention, location_brand, character_reference
  • brand_name (string, required) — Brand name to display
  • product_name (string, optional) — Specific product name
  • tagline (string, optional) — Brand tagline
  • narrative_hook (string, required for narrative_hook type) — Primary narrative text injected into the game
  • alt_hooks (array of strings, optional) — Alternative hooks for variety (system rotates automatically)
  • tone (string, optional) — One of: positive, neutral, humorous, dramatic. Default: neutral
  • max_uses_per_game (integer, optional) — Maximum times this creative can appear in a single game session. Range: 1-20. Default: 3
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/creatives \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "creative_type": "narrative_hook",
    "brand_name": "Acme Corp",
    "product_name": "Acme Shield",
    "tagline": "Protection you can trust",
    "narrative_hook": "A familiar logo on the briefing folder catches your eye — Acme Shield, the defense contractor everyone in Washington seems to know.",
    "alt_hooks": [
      "The advisor slides an Acme Shield dossier across the table.",
      "On the news ticker: Acme Shield wins another Pentagon contract."
    ],
    "tone": "neutral",
    "max_uses_per_game": 5
  }'

Response (201):

{
  "id": "crt_def456",
  "campaign_id": "camp_abc123",
  "creative_type": "narrative_hook",
  "brand_name": "Acme Corp",
  "product_name": "Acme Shield",
  "tagline": "Protection you can trust",
  "narrative_hook": "A familiar logo on the briefing folder catches your eye — Acme Shield, the defense contractor everyone in Washington seems to know.",
  "alt_hooks": [
    "The advisor slides an Acme Shield dossier across the table.",
    "On the news ticker: Acme Shield wins another Pentagon contract."
  ],
  "tone": "neutral",
  "max_uses_per_game": 5,
  "impressions": 0,
  "created_at": "2026-03-01T12:00:00Z"
}

List Creatives

GET /campaigns/{id}/creatives

List all creatives for a campaign.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/creatives \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Array of creative objects.

Update Creative

PATCH /creatives/{id}

Update creative fields. Only include fields you want to change.

Request body (all optional):

  • brand_name (string)
  • product_name (string)
  • tagline (string)
  • narrative_hook (string)
  • alt_hooks (array of strings)
  • tone (string)
  • max_uses_per_game (integer, 1-20)
curl -X PATCH https://api.scrya.com/api/ads/creatives/crt_def456 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "narrative_hook": "An Acme Shield briefcase sits on the Resolute desk, its logo catching the light.",
    "tone": "dramatic"
  }'

Response (200): Updated creative object.

Delete Creative

DELETE /creatives/{id}

Permanently delete a creative. Cannot delete the last creative on an active campaign.

curl -X DELETE https://api.scrya.com/api/ads/creatives/crt_def456 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (204): No content.

Affinities

Affinities control how the ad matching pipeline biases visual and narrative elements when your ad is selected. They influence the game's rendering pipeline — lighting, camera angles, effects, and more — to create subtle brand alignment without breaking immersion. See Affinity Targeting for details on each type.

List Affinities

GET /campaigns/{id}/affinities

List all affinities for a campaign.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/affinities \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Array of affinity objects.

Add Affinity

POST /campaigns/{id}/affinities

Request body:

  • affinity_type (string, required) — One of: action, location, theme, mood, category, lighting, effects, camera
  • affinity_value (string, required) — The target value (e.g., "warm golden hour" for lighting, "wide establishing" for camera)
  • weight (number, required) — Influence strength from 0.1 (subtle) to 3.0 (strong). Default: 1.0
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/affinities \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "affinity_type": "lighting",
    "affinity_value": "warm golden hour",
    "weight": 1.5
  }'

Response (201):

{
  "id": "aff_lmn456",
  "campaign_id": "camp_abc123",
  "affinity_type": "lighting",
  "affinity_value": "warm golden hour",
  "weight": 1.5,
  "created_at": "2026-03-01T12:00:00Z"
}

Remove Affinity

DELETE /campaigns/{id}/affinities/{aid}

Remove a single affinity.

curl -X DELETE https://api.scrya.com/api/ads/campaigns/camp_abc123/affinities/aff_lmn456 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (204): No content.

Bulk Replace Affinities

PUT /campaigns/{id}/affinities/bulk

Replace all affinities for a campaign in a single call. Existing affinities are deleted and replaced with the provided list. Maximum 50 affinities per campaign.

Request body:

  • affinities (array, required) — Array of affinity objects, each with affinity_type, affinity_value, and weight
curl -X PUT https://api.scrya.com/api/ads/campaigns/camp_abc123/affinities/bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "affinities": [
      {"affinity_type": "lighting", "affinity_value": "warm golden hour", "weight": 1.5},
      {"affinity_type": "mood", "affinity_value": "tense", "weight": 1.2},
      {"affinity_type": "camera", "affinity_value": "close-up", "weight": 0.8},
      {"affinity_type": "effects", "affinity_value": "film grain, cinematic widescreen", "weight": 1.0}
    ]
  }'

Response (200): Array of the newly created affinity objects.

Analytics

Retrieve campaign performance data. All analytics endpoints return data for the authenticated advertiser's campaigns only.

Campaign Stats

GET /campaigns/{id}/stats

Get aggregate statistics for a campaign.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/stats \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "campaign_id": "camp_abc123",
  "impressions": 15230,
  "unique_users": 8421,
  "unique_games": 12,
  "spent_cents": 38075,
  "budget_cents": 500000,
  "effective_cpm_cents": 250,
  "avg_affinity_score": 0.73,
  "top_creative_id": "crt_def456",
  "top_game_id": "presidential_dilemma"
}

Daily Time-Series

GET /campaigns/{id}/analytics/daily

Get daily impression and spend data as a time series.

Query parameters:

  • days (integer, optional) — Number of days to return. Range: 1-365. Default: 30
curl "https://api.scrya.com/api/ads/campaigns/camp_abc123/analytics/daily?days=7" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "campaign_id": "camp_abc123",
  "days": [
    {
      "date": "2026-02-28",
      "impressions": 2150,
      "unique_users": 1203,
      "spent_cents": 5375,
      "avg_affinity_score": 0.71
    },
    {
      "date": "2026-02-27",
      "impressions": 1980,
      "unique_users": 1105,
      "spent_cents": 4950,
      "avg_affinity_score": 0.74
    }
  ]
}

Per-Creative Breakdown

GET /campaigns/{id}/analytics/creatives

Get performance broken down by individual creative.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/analytics/creatives \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "campaign_id": "camp_abc123",
  "creatives": [
    {
      "creative_id": "crt_def456",
      "creative_type": "narrative_hook",
      "brand_name": "Acme Corp",
      "impressions": 9500,
      "unique_users": 5200,
      "spent_cents": 23750,
      "avg_affinity_score": 0.78
    },
    {
      "creative_id": "crt_ghi789",
      "creative_type": "product_mention",
      "brand_name": "Acme Corp",
      "impressions": 5730,
      "unique_users": 3221,
      "spent_cents": 14325,
      "avg_affinity_score": 0.65
    }
  ]
}

Per-Country Breakdown

GET /campaigns/{id}/analytics/geo

Get performance broken down by country.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/analytics/geo \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "campaign_id": "camp_abc123",
  "countries": [
    {
      "country_code": "US",
      "impressions": 10500,
      "unique_users": 5800,
      "spent_cents": 26250
    },
    {
      "country_code": "GB",
      "impressions": 2100,
      "unique_users": 1150,
      "spent_cents": 5250
    },
    {
      "country_code": "DE",
      "impressions": 1430,
      "unique_users": 780,
      "spent_cents": 3575
    }
  ]
}

Affinity Effectiveness

GET /campaigns/{id}/analytics/affinities

See how each affinity is performing. Shows match rate (how often the affinity contributed to ad selection) and average score contribution.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/analytics/affinities \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "campaign_id": "camp_abc123",
  "affinities": [
    {
      "affinity_id": "aff_lmn456",
      "affinity_type": "lighting",
      "affinity_value": "warm golden hour",
      "weight": 1.5,
      "match_rate": 0.42,
      "avg_score_contribution": 0.31,
      "impressions_influenced": 6397
    },
    {
      "affinity_id": "aff_opq789",
      "affinity_type": "mood",
      "affinity_value": "tense",
      "weight": 1.2,
      "match_rate": 0.68,
      "avg_score_contribution": 0.22,
      "impressions_influenced": 10356
    }
  ]
}

Budget Pacing

GET /campaigns/{id}/analytics/pacing

Get budget pacing information showing spend rate, projected exhaustion date, and daily cap utilization.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/analytics/pacing \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "campaign_id": "camp_abc123",
  "budget_cents": 500000,
  "spent_cents": 38075,
  "remaining_cents": 461925,
  "daily_cap_cents": 25000,
  "today_spent_cents": 12400,
  "today_remaining_cents": 12600,
  "avg_daily_spend_cents": 5439,
  "projected_exhaustion_date": "2026-05-25",
  "days_remaining": 85,
  "pacing_status": "under_pacing",
  "utilization_pct": 49.6
}

Placements (Developer)

Game developers use placement endpoints to register their games for ad serving, configure ad types, set revenue share, and control which advertisers and categories are allowed.

Enable Placements

POST /placements/{game_id}

Register a game for ad placements. The game_id is your unique identifier for the game.

Request body:

  • creator_id (string, required) — Your developer/creator user ID
  • placement_types (array of strings, required) — Accepted creative types (e.g., ["narrative_hook", "product_mention", "location_brand"])
  • revenue_share_pct (number, required) — Revenue share percentage for the developer. Range: 0-100
  • max_ads_per_session (integer, optional) — Maximum ads per game session. Range: 1-50. Default: 10
  • blocked_advertisers (array of strings, optional) — Advertiser IDs to block from this game
  • blocked_categories (array of strings, optional) — Ad categories to block (e.g., ["gambling", "alcohol"])
curl -X POST https://api.scrya.com/api/ads/placements/my_game_id \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "creator_id": "dev_user_123",
    "placement_types": ["narrative_hook", "product_mention"],
    "revenue_share_pct": 70,
    "max_ads_per_session": 5,
    "blocked_categories": ["gambling"]
  }'

Response (201):

{
  "game_id": "my_game_id",
  "creator_id": "dev_user_123",
  "placement_types": ["narrative_hook", "product_mention"],
  "revenue_share_pct": 70,
  "max_ads_per_session": 5,
  "blocked_advertisers": [],
  "blocked_categories": ["gambling"],
  "is_active": true,
  "created_at": "2026-03-01T12:00:00Z"
}

Get Placement

GET /placements/{game_id}

Get the current placement configuration for a game.

curl https://api.scrya.com/api/ads/placements/my_game_id \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Placement object.

Update Placement

PATCH /placements/{game_id}

Update placement configuration. Only include fields you want to change.

Request body (all optional):

  • placement_types (array of strings)
  • revenue_share_pct (number, 0-100)
  • max_ads_per_session (integer, 1-50)
  • blocked_advertisers (array of strings)
  • blocked_categories (array of strings)
curl -X PATCH https://api.scrya.com/api/ads/placements/my_game_id \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "max_ads_per_session": 8,
    "blocked_categories": ["gambling", "alcohol"]
  }'

Response (200): Updated placement object.

Disable Placements

DELETE /placements/{game_id}

Disable ad placements for a game. Stops all ad serving immediately. The configuration is preserved and can be re-enabled by calling POST again.

curl -X DELETE https://api.scrya.com/api/ads/placements/my_game_id \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (204): No content.

Revenue

Track ad spend (for advertisers) and earnings (for game developers).

Advertiser Spend

GET /revenue

Get total spend summary for the authenticated advertiser across all campaigns.

curl https://api.scrya.com/api/ads/revenue \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "total_spent_cents": 152300,
  "total_impressions": 60920,
  "active_campaigns": 3,
  "balance_cents": 347700,
  "campaigns": [
    {
      "campaign_id": "camp_abc123",
      "name": "Summer 2026 Launch",
      "spent_cents": 38075,
      "impressions": 15230,
      "status": "active"
    }
  ]
}

Creator Total Earnings

GET /creator-revenue

Get total earnings across all games for the authenticated developer.

curl https://api.scrya.com/api/ads/creator-revenue \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "total_earned_cents": 106610,
  "total_impressions": 60920,
  "games_count": 2,
  "games": [
    {
      "game_id": "my_game_id",
      "earned_cents": 85000,
      "impressions": 48500,
      "revenue_share_pct": 70
    },
    {
      "game_id": "my_other_game",
      "earned_cents": 21610,
      "impressions": 12420,
      "revenue_share_pct": 70
    }
  ]
}

Per-Game Earnings

GET /creator-revenue/{game_id}

Get detailed earnings for a specific game.

curl https://api.scrya.com/api/ads/creator-revenue/my_game_id \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "game_id": "my_game_id",
  "earned_cents": 85000,
  "impressions": 48500,
  "revenue_share_pct": 70,
  "top_advertisers": [
    {"advertiser_id": "adv_abc123", "impressions": 15230, "earned_cents": 26653},
    {"advertiser_id": "adv_xyz456", "impressions": 12100, "earned_cents": 21175}
  ],
  "daily": [
    {"date": "2026-02-28", "impressions": 2150, "earned_cents": 3763},
    {"date": "2026-02-27", "impressions": 1980, "earned_cents": 3465}
  ]
}

Simulator

Preview how your campaign's affinities would bias the rendering pipeline for a given scene context. Useful for testing and tuning affinities before activating a campaign.

Simulate Pipeline Bias

POST /campaigns/{id}/simulate

Preview the pipeline bias your campaign would produce for a given narrative context.

Request body:

  • narrative_function (string, optional) — Scene narrative context (e.g., "The president reviews classified documents")
  • mood (string, optional) — Scene mood (e.g., "tense", "hopeful", "dramatic")
curl -X POST https://api.scrya.com/api/ads/campaigns/camp_abc123/simulate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "narrative_function": "The president reviews classified documents in the Oval Office",
    "mood": "tense"
  }'

Response (200):

{
  "campaign_id": "camp_abc123",
  "matched_creative": {
    "id": "crt_def456",
    "narrative_hook": "A familiar logo on the briefing folder catches your eye — Acme Shield, the defense contractor everyone in Washington seems to know.",
    "creative_type": "narrative_hook"
  },
  "biased_pipeline": {
    "lighting": "warm golden hour",
    "effects": "film grain, cinematic widescreen",
    "camera": "close-up",
    "mood": "tense"
  },
  "affinity_scores": {
    "lighting": 0.63,
    "mood": 0.82,
    "camera": 0.24,
    "effects": 0.41
  },
  "total_score": 0.73
}

Get Simulation Options

GET /campaigns/{id}/simulate/options

Get available options for the simulation endpoint, including valid mood values, narrative function examples, and the campaign's current affinity configuration.

curl https://api.scrya.com/api/ads/campaigns/camp_abc123/simulate/options \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "moods": ["tense", "hopeful", "dramatic", "calm", "chaotic", "mysterious", "humorous", "melancholic"],
  "narrative_examples": [
    "The president addresses the nation from the Oval Office",
    "A secret meeting in the Pentagon basement",
    "Campaign rally in a small Midwestern town",
    "Trade negotiations with foreign diplomats"
  ],
  "current_affinities": [
    {"affinity_type": "lighting", "affinity_value": "warm golden hour", "weight": 1.5},
    {"affinity_type": "mood", "affinity_value": "tense", "weight": 1.2}
  ]
}

Ad Matching

The core integration endpoints. Call /match when generating a scene to find the best ad, then call /impressions after the player sees the ad-influenced content. See the Quickstart for a complete walkthrough.

Find Best Ad

POST /match

Find the best matching ad for a given game context. The matching pipeline evaluates all active campaigns against the provided context, applying geo targeting, blocklist rules, affinity scoring, and budget constraints. Returns the winning creative with pipeline bias values, or 204 No Content if no ads match.

Request body:

  • game_id (string, required) — Your registered game ID
  • user_id (string, required) — Current player's user ID (for frequency capping)
  • player_geo (object, optional) — Player geographic data
    • country_code (string) — ISO 3166-1 alpha-2 code
    • state_code (string) — State/province code
    • city_name (string) — City name
    • lat (number) — Latitude
    • lng (number) — Longitude
  • game_context (object, required) — Current scene context
    • theme_id (string) — Active game theme
    • category (string) — Game category
    • narrative (string) — Current narrative text or scene description
    • mood_tags (array of strings) — Current mood tags
    • location (string) — Current game location
    • actions (array of strings) — Available player actions
  • session_id (string, optional) — Game session ID (for per-session frequency capping)
curl -X POST https://api.scrya.com/api/ads/match \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "game_id": "my_game_id",
    "user_id": "player_123",
    "player_geo": {
      "country_code": "US",
      "state_code": "CA"
    },
    "game_context": {
      "theme_id": "1",
      "category": "simulation",
      "narrative": "The president faces a difficult decision about defense spending",
      "mood_tags": ["tense", "dramatic"],
      "location": "oval_office",
      "actions": ["approve_budget", "reject_budget", "defer"]
    },
    "session_id": "sess_abc"
  }'

Response (200):

{
  "campaign_id": "camp_abc123",
  "creative_id": "crt_def456",
  "narrative_hook": "A familiar logo on the briefing folder catches your eye — Acme Shield, the defense contractor everyone in Washington seems to know.",
  "creative_type": "narrative_hook",
  "brand_name": "Acme Corp",
  "product_name": "Acme Shield",
  "tagline": "Protection you can trust",
  "tone": "neutral",
  "biased_pipeline": {
    "lighting": "warm golden hour",
    "effects": "film grain, cinematic widescreen",
    "camera": "close-up",
    "mood": "tense"
  },
  "affinity_score": 0.73,
  "cpm_bid_cents": 250
}

Response (204): No content. No matching ads found — proceed with normal scene generation.

Record Impression

POST /impressions

Record that a player saw ad-influenced content. This triggers billing (the advertiser is charged and the developer earns their revenue share). Call this after the player has actually seen the narrative hook or ad-influenced scene.

Request body:

  • campaign_id (string, required) — Campaign ID from the match response
  • creative_id (string, required) — Creative ID from the match response
  • game_id (string, required) — Your game ID
curl -X POST https://api.scrya.com/api/ads/impressions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "camp_abc123",
    "creative_id": "crt_def456",
    "game_id": "my_game_id"
  }'

Response (201):

{
  "impression_id": "imp_uvw123",
  "campaign_id": "camp_abc123",
  "creative_id": "crt_def456",
  "game_id": "my_game_id",
  "charged_cents": 0.25,
  "recorded_at": "2026-03-01T12:05:00Z"
}

Rate Limits

API requests are rate-limited per API key:

  • Match endpoint: 1,000 requests/minute (designed for real-time game integration)
  • Impressions endpoint: 1,000 requests/minute
  • Management endpoints: 100 requests/minute (campaigns, creatives, targeting)
  • Analytics endpoints: 60 requests/minute

Rate limit headers are included in every response:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1709294460

When rate limited, you receive 429 Too Many Requests with a Retry-After header in seconds.

Error Reference

All errors return a JSON body with a detail field:

{
  "detail": "Campaign budget_cents cannot be less than current spent_cents (38075)"
}
Status Meaning Common Causes
400 Bad Request Invalid field value, missing required field
401 Unauthorized Missing or invalid API key
403 Forbidden Accessing another advertiser's resource
404 Not Found Campaign, creative, or placement does not exist
409 Conflict Duplicate registration, campaign already active
422 Validation Error cpm_bid_cents out of range, invalid affinity_type, too many affinities
429 Rate Limited Too many requests, check Retry-After header
500 Server Error Internal error, contact support

What's Next