Skip to content
Adscod

Developer Platform · v1

API Reference

Programmatic access to Adscod's ad network: advertisers create and manage CPC/CPA/CPM campaigns, and publishers fetch matched native ads with a single Serve call or a one-line script embed. No SDK install required for either side.

Base URLhttps://api.adscod.com/api/v1

Authentication

This page covers the ad-network API: two independent key types, one per side of the network. Use the one that matches your account; they are not interchangeable.

Advertiser key – adc_adv_…

For creating and managing campaigns. Generate one from Advertiser dashboard → Settings → API keys.

curl
curl "https://api.adscod.com/api/v1/external/campaigns" \
   -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE"

Publisher key – adc_pub_…

For requesting ads to serve on your site. Generate one from Publisher dashboard → API keys.

curl
curl "https://api.adscod.com/api/v1/serve?source=publisher&publisherKey=adc_pub_YOUR_KEY" 

Never expose keys in client-side code. The Publisher SDK script tag is the one exception: it's designed to carry your adc_pub_… key publicly, the same way an analytics snippet does.

There is no creator key. Creators don't integrate programmatically; campaigns are activated on a bio page from the in-app Creator Hub, and the engine matches and serves ads to that page automatically. If you're building a creator-side integration, there's currently no API for it.

Looking for fraud lookups, webhooks, organization/agency-brand data, or escrow? Those live on a separate ak_live_… key (self-service — POST https://api.adscod.com/api/v1/register, no auth required), not the two key types on this page. Fully documented in the Swagger UI.

Campaigns

adc_adv_ required

Manage CPC, CPA, or CPM campaigns for the advertiser account linked to your API key. Campaigns are created as DRAFT and start serving once you fund your ad wallet (dashboard-only; there is no funding endpoint on this API) and set action: "publish".

POSThttps://api.adscod.com/api/v1/external/campaigns

Create a new campaign. Always starts as DRAFT; publish it with a separate PATCH once your wallet is funded.

Request Body

namestringrequiredInternal campaign name
adTitlestringrequiredHeadline shown to viewers
adBodystringrequiredAd copy shown to viewers
ctaUrlstringrequiredLanding page the click redirects to
ctaLabelstringButton text. Default "Learn More"
adImageUrlstringCreative image URL
budgetUsdnumberrequiredTotal campaign budget
pricingModelenumCPC | CPA | CPM. Default CPC
cpcBidUsdnumberCost per click: required if pricingModel is CPC
cpaBidUsdnumberCost per conversion: required if pricingModel is CPA. Charged at conversion postback, not at click
cpmRateUsdnumberRate per 1000 impressions: required if pricingModel is CPM. Charged per impression at serve time, not per click
actionTypeenumINSTALL | SIGNUP | PURCHASE | LEAD | CUSTOM. CPA conversion type, defaults to CUSTOM
postbackUrlstringYour URL to receive a copy of each conversion (HTTPS only)
dailyCapUsdnumberMax daily spend. Minimum $0.01 if set
categorystringUsed for intent matching at serve time
keywordsstring[]Used for intent matching at serve time
targetCountriesstring[]ISO country codes. Omitted = all countries
targetDevicesstring[]MOBILE | DESKTOP | TABLET. Omitted = all devices
scheduleDaysnumber[]0–6 (Sun–Sat) dayparting. Omitted = every day
scheduleHoursnumber[]0–23 dayparting. Omitted = all hours
frequencyCapnumberMax impressions per viewer per window. Omitted = uncapped

Example Request

curl
curl -X POST https://api.adscod.com/api/v1/external/campaigns \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer Launch",
    "adTitle": "Shop the new collection",
    "adBody": "Up to 40% off this week only.",
    "ctaUrl": "https://example.com/deals",
    "budgetUsd": 50,
    "cpcBidUsd": 0.12,
    "targetCountries": ["NG", "KE"]
  }'

Response

json
{
  "success": true,
  "campaign": {
    "id": "cmp_abc123",
    "name": "Summer Launch",
    "adTitle": "Shop the new collection",
    "pricingModel": "CPC",
    "budgetUsd": 50,
    "cpcBidUsd": 0.12,
    "status": "DRAFT",
    "createdAt": "2026-06-30T10:00:00Z"
  }
}
GEThttps://api.adscod.com/api/v1/external/campaigns

List your campaigns with live stats, most recently created first. Capped at your 50 most recent campaigns — no pagination on this endpoint yet, so use GET /external/campaigns/:id for anything older.

Example Request

curl
curl "https://api.adscod.com/api/v1/external/campaigns" \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE"

Response

json
{
  "success": true,
  "campaigns": [ { "id": "cmp_abc123", "name": "Summer Launch", "status": "ACTIVE", "budgetUsd": 50, "cpcBidUsd": 0.12 } ],
  "stats": { "totalImpressions": 18400, "totalClicks": 340, "totalConversions": 12, "totalSpent": 40.80, "totalBudget": 50, "ctr": 1.85, "conversionRate": 3.53 }
}
GEThttps://api.adscod.com/api/v1/external/campaigns/:id

Get a single campaign you own, plus its 14-day click/spend timeseries.

Parameters

idstringrequiredCampaign ID

Example Request

curl
curl "https://api.adscod.com/api/v1/external/campaigns/cmp_abc123" \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE"

Response

json
{
  "success": true,
  "campaign": { "id": "cmp_abc123", "name": "Summer Launch", "status": "ACTIVE", "budgetUsd": 50, "cpcBidUsd": 0.12, "targetCountries": ["NG", "KE"] },
  "timeseries": [ { "date": "2026-06-29", "clicks": 24, "spend": 2.88 } ]
}
PATCHhttps://api.adscod.com/api/v1/external/campaigns/:id

Update budget, bid, targeting, or copy, or pass action to change lifecycle status. Publishing or resuming requires a funded ad wallet.

Request Body

actionenumpublish | pause | resume | archive
namestringSame as create
adTitlestringSame as create
adBodystringSame as create
budgetUsdnumberSame as create
cpcBidUsdnumberSame as create
dailyCapUsdnumberSame as create
targetCountriesstring[]Same as create
keywordsstring[]Same as create

Example Request

curl
curl -X PATCH https://api.adscod.com/api/v1/external/campaigns/cmp_abc123 \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{ "action": "publish" }'

Response

json
{ "success": true, "id": "cmp_abc123", "status": "ACTIVE" }
POSThttps://api.adscod.com/api/v1/external/campaigns/conversions

Server-to-server: report a conversion for a CPA campaign once your backend has confirmed it. Pass back the click's attribution token — the query param it arrives on differs by traffic source: aff_click_id for a creator's bio-page click, adscod_click for a publisher click through the Orbis engine's /click/:token (see Ad Serving below). Rejected if the click is more than 30 days old. Idempotent: reporting the same clickId twice is a no-op the second time (alreadyRecorded: true, charged: false).

Request Body

clickIdstringrequiredThe click's attribution token — aff_click_id (creator traffic) or adscod_click (publisher traffic), whichever your landing page received
typestringConversion type. Defaults to the campaign's actionType
valuenumberConversion value, for analytics

Example Request

curl
curl -X POST https://api.adscod.com/api/v1/external/campaigns/conversions \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{ "clickId": "clk_9f2a", "type": "PURCHASE", "value": 24.99 }'

Response

json
{ "success": true, "alreadyRecorded": false, "charged": true }

Read-only. Creators choose which campaigns to activate themselves from the Creator Hub; there is no endpoint to assign or invite a creator to a campaign.

GEThttps://api.adscod.com/api/v1/external/campaigns/:id/creators

List creators who have activated this campaign on their bio page, with each creator's clicks and earnings.

Parameters

idstringrequiredCampaign ID

Example Request

curl
curl "https://api.adscod.com/api/v1/external/campaigns/cmp_abc123/creators" \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE"

Response

json
{
  "success": true,
  "campaignId": "cmp_abc123",
  "total": 1,
  "creators": [
    {
      "creatorId": "crt_1",
      "username": "janedoe",
      "displayName": "Jane Doe",
      "verified": true,
      "country": "NG",
      "niche": "fashion",
      "followers": 1000000,
      "isActive": true,
      "clicks": 340,
      "earnedUsd": 26.52,
      "activatedAt": "2026-06-20T09:00:00Z"
    }
  ]
}
GEThttps://api.adscod.com/api/v1/external/creators

Browse the public creator pool: gauge available inventory by niche, follower count, or country before launching a campaign. Ranked by real ad performance (conversion rate, CTR) by default — pass sort=followers or sort=newest for a raw-metric ordering instead.

Parameters

qstringSearch by name or @username
nichestringe.g. fashion, fintech, gaming
countrystringISO country code
minFollowersnumberMinimum follower count
maxFollowersnumberMaximum follower count
sortenumperformance | followers | newest. Default performance
pagenumberDefault 1
limitnumberDefault 20, max 50

Example Request

curl
curl "https://api.adscod.com/api/v1/external/creators?niche=fashion&country=NG&minFollowers=100000" \
  -H "Authorization: Bearer adc_adv_YOUR_KEY_HERE"

Response

json
{
  "success": true,
  "creators": [
    { "id": "crt_1", "username": "janedoe", "displayName": "Jane Doe", "verified": true, "country": "NG", "niche": "fashion", "followers": 1000000 }
  ],
  "total": 1, "page": 1, "totalPages": 1
}

Ad Serving (Orbis)

adc_pub_ required

The serving hot path is a separate Go service at https://api.adscod.com, not the https://api.adscod.com/api/v1 host used above. It matches active campaigns to a context and returns a short-lived clickUrl; the matching is always a CPC-bid auction boosted by intent/category overlap, not a hard filter, so empty inventory never blocks a response.

Pricing models & payouts

CPC: billed atomically at /click/:token: your cpcBidUsd is debited from the advertiser wallet the instant a click is billed, 65% credited to the creator/publisher wallet, 35% to Adscod.

CPA: the click itself is free: /click/:token records a zero-value attribution row and redirects, no money moves. The charge (and the same 65/35 payout split, applied to cpaBidUsd) happens when the conversion is reported, either POST /api/v1/conversion below, or POST /external/campaigns/conversions from your backend.

CPM: billed on viewability, not on serve: a matched CPM ad is returned for free, and cpmRateUsd / 1000 (same 65/35 split) is charged only once the ad is actually viewed, when your SDK's dwell beacon (POST /api/v1/dwell) reports it as seen. So you are never charged for ads that were returned but never rendered, and a limit=10 request that renders 2 bills 2, not 10. Fire the dwell beacon (the SDK does this automatically) or CPM impressions won't bill.

The auction itself always ranks on one number per campaign: cpcBidUsd for CPC/CPA campaigns, cpmRateUsd / 1000 (its per-impression value) for CPM, so campaigns on any pricing model compete for the same inventory on a comparable basis.

GEThttps://api.adscod.com/api/v1/serve

Match active campaigns to a context and return the best ad(s) plus a one-time clickUrl. This is what the Publisher SDK calls under the hood; call it directly only if you need server-side rendering or custom UI.

Parameters

sourceenumrequiredpublisher (creators use the bio-page flow, not this key)
publisherKeystringrequiredadc_pub_… key. Prefer the X-Adscod-Key header over this query param
placementIdstringYour AdPlacement ID, if you've created one
placementstringSDK slot hint (e.g. "video", "sidebar") sent by the Publisher SDK on every request — separate from placementId, and what unlocks the video/audio slot
countrystringISO-3166 alpha-2. Falls back to geo header
devicestringMOBILE | DESKTOP | TABLET
citystringFalls back to geo header, then a self-hosted GeoLite2 lookup if configured
categorystringSoft filter: boosts matching campaigns, doesn't exclude others
qstringRaw search/page-context string, e.g. "cheap bus nairobi mombasa"
limitnumberDefault 1, max 10

Example Request

curl
curl "https://api.adscod.com/api/v1/serve?source=publisher&placementId=plc_789&country=NG&device=MOBILE&limit=1" \
  -H "X-Adscod-Key: adc_pub_YOUR_KEY_HERE"

Response

json
{
  "success": true,
  "ads": [
    {
      "campaignId": "cmp_abc123",
      "title": "Shop the new collection",
      "body": "Up to 40% off this week only.",
      "imageUrl": "https://…",
      "ctaLabel": "Shop now",
      "clickUrl": "https://adscod.com/click/<clickToken>",
      "cpcUsd": "0.045000",
      "matchContext": "Relevant to this placement's category",
      "brandName": "Acme Co",
      "brandLogoUrl": "https://…",
      "brandVerified": true
    }
  ]
}

A few fields only appear conditionally, depending on the matched campaign: videoUrl/audioUrl for video/audio placements, adPrice/adCompareAtPrice/adCurrency for a priced ad, and products (an array) for a shopping/product-feed campaign. Don't assume any of these are always present.

GEThttps://adscod.com/click/:token

The clickUrl from /serve. For CPC campaigns, bills the click (65% to you, 35% platform); for CPA, records a free attribution row instead — see the pricing note above. Either way it 302-redirects the viewer to the advertiser's ctaUrl with ?adscod_click=<ref> appended, which your landing page needs to report a conversion via POST /external/campaigns/conversions or POST /api/v1/conversion below. No JSON response; never call this from your backend, only link/redirect viewers to it. Billing failures degrade to a plain redirect, so a viewer never sees an error.

Parameters

tokenstringrequiredThe clickToken embedded in the clickUrl returned by /serve

Example Request

curl
# Not called directly: render clickUrl as a normal <a href> from the /serve response

Response

json
302 Found
Location: https://example.com/deals?adscod_click=clk_9f2a
POSThttps://api.adscod.com/api/v1/conversion

Idempotent conversion postback for the hot-path engine. Fired server-to-server from the advertiser's backend with their secret API key (X-Adscod-Key header, or advertiserKey query param — prefer the header) and the click token from the click-through. The key must never be exposed in browser code. A conversion only matches clicks on your own campaigns. Distinct from /external/campaigns/conversions; use that one for batch S2S CPA reporting instead.

Request Body

clickTokenstringrequiredThe token from the original click
typestringConversion type, for analytics
valuenumberConversion value, for analytics

Example Request

curl
curl -X POST https://api.adscod.com/api/v1/conversion \
  -H "Content-Type: application/json" \
  -H "X-Adscod-Key: <YOUR_ADSCOD_API_KEY>" \
  -d '{ "clickToken": "<token>", "value": 12.50 }'

Response

json
{ "success": true, "converted": true, "duplicate": false }

Publisher SDK

no API calls required

For most publishers, the script tag is simpler than calling /api/v1/serve directly: it auto-detects page category/intent from your page's own meta tags and renders a styled native ad card with no layout work on your side.

html
<script
   src="https://api.adscod.com/sdk/v1/publisher.js"
   data-key="adc_pub_YOUR_KEY_HERE"
   data-placement="banner"
   async
></script>

Script attributes

data-keyRequired. Your adc_pub_… publisher key.
data-placementbanner | sidebar | inline | native. Default banner.
data-targetCSS selector of an element to render the ad into (e.g. "#ad-slot"). Without this, the ad renders as the next sibling of the <script> tag — fragile if a script loader (Next.js Script, GTM) relocates that tag in the DOM.
data-categoryPage category. Auto-detected from <meta name="category"> if omitted.
data-limitHow many ads to render, 1–3. Default 1.
data-engineOrbis (serve) base URL override. Default https://api.adscod.com.
data-apiAPI base URL for config/templates. Default https://api.adscod.com.
data-templatePresence enables native mode: renders through your own template (Publisher dashboard → Ad Templates) instead of the built-in card. Falls back to the built-in card if no template is configured for the slot.

Errors and Rate Limits

HTTP Status Codes

200/201Success
400Validation error: check the response body for details
401Invalid or missing API key
403Key is revoked, or the resource doesn't belong to your account
404Resource not found
429Rate limit exceeded: back off and retry
500Server error: contact support@adscod.com

Rate Limits

The https://api.adscod.com/api/v1 host (Campaigns, Creators) is limited to 300 requests/minute, same for every key. The separate https://api.adscod.com host has its own, per-endpoint limits: /api/v1/serve allows 100 req/10s per IP and 300 req/10s per publisher key, and /click/:token allows 40 req/10s per IP. All return HTTP 429 with a Retry-After header once exceeded.

Error Shape — https://api.adscod.com/api/v1

json
{
  "statusCode": 403,
  "message": "API key has been revoked",
  "error": "Forbidden"
}

Error Shape — https://api.adscod.com

json
{
  "success": false,
  "error": "invalid publisher API key"
}