API Documentation
Everything in the dashboard is available over the API. Manage campaigns, creatives, and zones, pull statistics, receive conversions, and subscribe to events.
01 Overview
The D4RKW3B API is a REST API over HTTPS. All requests and responses use JSON with UTF-8 encoding. Timestamps are ISO 8601 in UTC. Monetary values are decimal strings in US dollars (for example "12.50") to avoid floating-point rounding. Identifiers are opaque strings and should not be parsed.
The current version is v1, carried in the URL path. Backwards-compatible changes (new fields, new endpoints, new enum values) are made without a version change; clients should ignore unknown fields. Breaking changes will be released under v2 with at least 12 months of parallel support for v1.
TLS 1.2 or higher is required. HTTP requests are rejected, not redirected.
02 Authentication
Create API keys under Settings → API Keys in the dashboard. Each key has one of three scopes:
| Scope | Access |
|---|---|
advertiser | Full read and write on campaigns, creatives, targeting, and advertiser statistics; read on balance |
publisher | Full read and write on zones and tags; read on publisher statistics and payouts |
read | Read-only on everything the account can see; suitable for reporting integrations |
Send the key as a bearer token in the Authorization header on every request.
Authorization: Bearer d4k_live_8f2a91c4e7b3d6f0a1c5e9b2d4f7a3c8
Keys are shown once at creation. Rotate keys from the dashboard; a rotated key remains valid for 24 hours so that deployments can be updated without downtime. Keys inherit the permissions of the user who created them and are revoked when that user is removed from the account. Never embed a key in client-side code or in a publisher tag.
03 Rate Limits
Limits apply per API key over a rolling 60-second window.
| Endpoint group | Limit |
|---|---|
Read endpoints (GET) | 600 requests per minute |
Write endpoints (POST, PATCH, DELETE) | 120 requests per minute |
| Statistics endpoints | 60 requests per minute, maximum 31-day range per request |
| Creative uploads | 30 requests per minute, 20 MB per file |
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp) headers. When a limit is exceeded the API returns 429 Too Many Requests with a Retry-After header in seconds. Clients should back off exponentially and never retry a 429 immediately. Higher limits are available for managed-service accounts on request.
04 Errors
Errors use standard HTTP status codes and a consistent body.
{
"error": {
"code": "validation_failed",
"message": "targeting.geo must contain at least one country code",
"field": "targeting.geo",
"request_id": "req_01J9X4M2K7P3Q8R5"
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_failed | A field is missing or invalid; field identifies it |
| 401 | unauthenticated | Missing, malformed, revoked, or expired API key |
| 403 | forbidden | The key's scope does not permit this action, or the account is restricted |
| 404 | not_found | The resource does not exist or belongs to another account |
| 409 | conflict | The action conflicts with current state, for example activating a campaign with no approved creatives |
| 402 | insufficient_balance | The advertiser balance cannot cover the requested action |
| 422 | policy_violation | The creative or landing page was rejected at automated pre-screen; message explains why |
| 429 | rate_limited | Rate limit exceeded; honour Retry-After |
| 500 | internal_error | Unexpected failure; retry with backoff and quote request_id to support if it persists |
| 503 | maintenance | Planned maintenance in progress; see the status page |
Include the request_id in any support request about a failed call.
05 Pagination
List endpoints return up to limit items (default 50, maximum 200) and a next_cursor when more results exist. Pass it back as cursor to fetch the next page. Cursors are valid for 10 minutes.
{
"data": [ ... ],
"next_cursor": "eyJpZCI6ImNtcF8wMUo5WDQifQ",
"has_more": true
}
06 Campaigns
A campaign groups creatives, targeting, a pricing model, a bid, and budgets. Campaigns are created in draft status, move to review when activated, and serve when status is active. Scope: advertiser.
List campaigns. Filters: status (draft, review, active, paused, rejected, completed), vertical, format.
Create a campaign.
{
"name": "Casino DE push Q4",
"vertical": "igaming",
"format": "push",
"pricing_model": "cpc",
"bid": "0.085",
"daily_budget": "500.00",
"total_budget": "15000.00",
"landing_url": "https://example-casino.com/lp/de?click_id={click_id}&zone={zone_id}",
"targeting": {
"geo": ["DE", "AT"],
"device": ["mobile"],
"os": ["android"],
"frequency_cap": { "impressions": 3, "per_hours": 24 },
"schedule": { "timezone": "Europe/Berlin", "hours": [10, 11, 12, 18, 19, 20, 21, 22] }
}
}
| Field | Type | Notes |
|---|---|---|
name | string | Required, 1 to 120 characters |
vertical | enum | Required. igaming, sportsbook, crypto, adult, sweeps, nutra, dating, forex, vpn, streaming, mainstream |
format | enum | Required. display, pop, push, native, interstitial, video |
pricing_model | enum | Required. cpm, cpc, or cpa (CPA requires an approved postback integration) |
bid | decimal string | Required. Per 1,000 impressions for CPM, per click for CPC, per conversion for CPA |
daily_budget | decimal string | Required, minimum "10.00" |
total_budget | decimal string | Optional; campaign completes when reached |
landing_url | string | Required. HTTPS. May contain tracking macros; see section 13 |
targeting | object | Required; see section 8 |
start_at, end_at | ISO 8601 | Optional scheduling bounds |
Retrieve a campaign, including its status, review object (reviewer notes and rejection reason if any), spend to date, and attached creative IDs.
Update any field from the create body. Changing landing_url or vertical on an active campaign returns it to review. Bid and budget changes apply within 60 seconds without re-review.
Submit a draft for review, or resume a paused campaign. Requires at least one approved creative and sufficient balance for one day of the daily budget; otherwise returns 409 or 402.
Stop delivery. Takes effect within 60 seconds. Spend already committed in open auctions may still be billed.
Archive a campaign. Archived campaigns stop serving, remain visible in statistics, and cannot be reactivated.
07 Creatives
Creatives belong to a campaign and are reviewed individually. A campaign serves only its approved creatives. Scope: advertiser.
Upload a creative as multipart/form-data. Fields depend on format.
| Format | Fields | Specifications |
|---|---|---|
display | file | JPG, PNG, GIF, or HTML5 ZIP. Sizes 300x250, 728x90, 160x600, 320x50, 300x600, 336x280. Max 500 KB (2 MB for HTML5). |
push | title, body, icon, image | Title max 30 characters, body max 45. Icon 192x192 PNG. Image 360x240 JPG or PNG, optional. |
native | title, description, image, brand | Title max 60, description max 120, brand max 25. Image 600x400 JPG or PNG. |
pop | none | Pop campaigns serve the landing URL directly; no creative upload needed. |
interstitial | file | JPG or PNG at 320x480, 480x320, 768x1024, or 1024x768. Max 800 KB. |
video | file or vast_url | MP4 H.264, 16:9, 6 to 30 seconds, max 20 MB; or a VAST 4.x tag URL. |
The response includes the creative id and status: "review". Automated pre-screen failures return 422 immediately with the reason.
List creatives with status (review, approved, rejected, archived), review notes, and per-creative CTR for the last 7 days.
Archive a creative. It stops serving immediately.
08 Targeting Object
All targeting fields are optional except geo. Omitted fields mean "all". Lists are inclusive; use exclude_ prefixed fields for exclusions.
| Field | Type | Values |
|---|---|---|
geo | array | ISO 3166-1 alpha-2 country codes. Required. Restricted verticals are automatically limited to markets permitted for the advertiser's verified licences. |
region | array | ISO 3166-2 subdivision codes, for example US-CA |
city | array | City names as listed by GET /targeting/cities?geo=DE |
device | array | desktop, mobile, tablet |
os | array | windows, macos, linux, android, ios, other |
os_version_min | string | Minimum major version, for example "13" |
browser | array | chrome, safari, firefox, edge, samsung, opera, other |
connection | array | wifi, cellular |
carrier | array | Mobile carrier IDs from GET /targeting/carriers?geo=BR |
language | array | ISO 639-1 browser language codes |
zones, exclude_zones | array | Zone IDs to whitelist or blacklist. Zone-level performance is in statistics with group_by=zone. |
publishers, exclude_publishers | array | Publisher IDs |
frequency_cap | object | { "impressions": n, "per_hours": h }. Default 3 per 24 hours. Pop and interstitial are additionally capped at 1 per session network-wide. |
schedule | object | timezone (IANA name), hours (array of 0 to 23), days (array of mon to sun) |
ip_ranges, exclude_ip_ranges | array | CIDR blocks, maximum 500 entries |
adult_inventory | boolean | Whether to serve on adult-flagged inventory. Forced true for adult campaigns and forced false for mainstream campaigns. |
09 Zones
A zone is one ad placement on one publisher property. Zones are created under a verified property and are activated after compliance review and the 72-hour traffic audit. Scope: publisher.
List your verified properties (domains, apps, push lists) with verification status and vertical flags.
List zones. Filters: property_id, format, status (pending, audit, active, paused, rejected).
{
"property_id": "prp_01J9X4HN2Q",
"name": "Article sidebar 300x250",
"format": "display",
"size": "300x250",
"pricing_preference": "auto",
"accept_verticals": ["mainstream", "igaming", "crypto", "vpn"],
"fallback_url": "https://yoursite.com/house-ad"
}
| Field | Type | Notes |
|---|---|---|
property_id | string | Required; must be verified |
name | string | Required, for your reference |
format | enum | Required. display, pop, push, native, interstitial, video, direct, smartlink |
size | string | Required for display |
pricing_preference | enum | cpm, cpc, revshare, auto (default) |
accept_verticals | array | Verticals this zone will serve. adult can be included only on properties flagged adult by compliance. |
fallback_url | string | Optional. Loaded when no ad fills, instead of a blank slot. |
floor_cpm | decimal string | Optional minimum CPM. Setting a floor reduces fill. |
Returns the ready-to-paste tag for the zone in html, plus the direct_url or smartlink_url for link formats.
Update name, pricing preference, accepted verticals, fallback URL, or floor. Adding adult to accept_verticals triggers a property re-review.
Stop serving on the zone. Resume with /activate.
10 Statistics
Statistics are aggregated hourly and are final for a day at 04:00 UTC the following day, after invalid traffic review. Figures before that point are provisional and may decrease. Scope: any.
| Parameter | Notes |
|---|---|
from, to | Required. Dates (YYYY-MM-DD) or ISO timestamps for hourly ranges. Maximum 31 days. |
group_by | Comma-separated. date, hour, campaign, creative, zone, publisher, geo, region, device, os, browser, format, vertical, sub1 to sub5. Maximum 4 dimensions. |
campaign_id, zone_id, geo, format | Optional filters, comma-separated |
metrics | Optional subset of: impressions, viewable_impressions, clicks, ctr, conversions, cr, spend, revenue, ecpm, ecpc, ecpa, ivt_filtered. Advertisers see spend; publishers see revenue. |
format | Response format: json (default) or csv |
{
"data": [
{
"date": "2026-10-21", "geo": "DE",
"impressions": 184203, "clicks": 4791, "ctr": 0.026,
"conversions": 61, "cr": 0.0127,
"spend": "407.24", "ecpa": "6.68",
"ivt_filtered": 2117
}
],
"final": true
}
11 Balance and Payouts
Advertiser balance, pending deposits, today's spend, and the estimated hours of delivery remaining at the current spend rate. Scope: advertiser or read.
Deposits, refunds, credits, and daily spend debits with timestamps and references. Supports from, to, and pagination.
Publisher payout history: period, gross earnings, IVT deductions, net amount, method, destination (masked), status (scheduled, processing, sent, held), and transaction reference or on-chain hash once sent. Scope: publisher or read.
The current unpaid balance, the next scheduled payout date, and whether the minimum threshold has been reached.
12 S2S Postbacks
Server-to-server postbacks report conversions to D4RKW3B so that CPA campaigns are billed correctly, smartlink routing learns which offers convert, and conversion columns populate in statistics. The flow:
- Include
{click_id}in your campaign'slanding_url. D4RKW3B replaces it with a unique identifier on each click. - Store the click identifier on your side (cookie, session, or your tracker's sub-ID field).
- When a conversion occurs, call the postback URL from your server with that identifier.
https://pb.d4rkw3b.network/conv?click_id={click_id}&event={event}&payout={payout}&txid={transaction_id}
| Parameter | Required | Notes |
|---|---|---|
click_id | Yes | The value D4RKW3B inserted into your landing URL |
event | No | lead, registration, deposit, sale, install, subscribe, or a custom name up to 32 characters. Defaults to conversion. |
payout | No | Decimal value of the conversion in USD, used for revenue reporting and smartlink optimisation |
txid | No | Your transaction identifier. Postbacks with a repeated click_id and txid are deduplicated. |
status | No | approved (default), pending, or rejected. Send a second postback to update a pending conversion. |
The endpoint accepts GET and POST, responds with 200 OK and a body of {"status":"ok"}, and processes conversions within 60 seconds. Conversions are accepted up to 30 days after the click. Personal data in any parameter is stripped and logged as a policy warning on the account. Postbacks may optionally be signed by appending &sig= with an HMAC-SHA256 of the query string using your postback secret from Settings; signed postbacks are required for CPA campaigns above USD 5,000 per day.
12.1 Tracker presets
The dashboard provides one-click templates for Voluum, Bemob, RedTrack, Binom, Keitaro, and ClickFlare that populate both the landing URL macros and the tracker's postback URL. The equivalent manual configuration is a landing URL of the form https://your-tracker.com/path?d4_click={click_id}&zone={zone_id}&geo={geo} and a tracker postback URL of https://pb.d4rkw3b.network/conv?click_id={d4_click}&payout={payout}, substituting the tracker's own macro syntax for the right-hand side.
13 Tracking Macros
Macros can be placed in any campaign landing_url and are replaced at click time. Values are URL-encoded.
| Macro | Value |
|---|---|
{click_id} | Unique click identifier for postbacks |
{campaign_id} | Campaign ID |
{creative_id} | Creative ID |
{zone_id} | Publisher zone that served the ad. Use for zone-level optimisation and whitelists. |
{publisher_id} | Publisher ID |
{geo} | Country code |
{region} | Subdivision code |
{city} | City name |
{device} | desktop, mobile, or tablet |
{os} | Operating system |
{os_version} | Major OS version |
{browser} | Browser family |
{language} | Browser language |
{carrier} | Mobile carrier name, or empty on Wi-Fi |
{connection} | wifi or cellular |
{format} | Ad format |
{bid} | Winning bid for this impression or click, in USD |
{timestamp} | Unix timestamp of the click |
{sub1} to {sub5} | Publisher-supplied sub-IDs passed through from the tag or smartlink |
14 Universal Tag
The universal tag loads once per page and renders every zone configured for the property. It is asynchronous, weighs under 12 KB compressed, and does not block page rendering.
<!-- D4RKW3B universal tag: place once before </body> --> <script async src="https://cdn.d4rkw3b.network/u.js" data-pub="PUB-7Q2K9M" data-zone="auto" data-sub1="homepage"></script> <!-- Display placement: one per display zone --> <ins class="d4-ad" data-zone="zn_01J9X5A8" style="display:inline-block;width:300px;height:250px"></ins>
| Attribute | Notes |
|---|---|
data-pub | Required. Your publisher ID. |
data-zone | auto renders every zone configured for the property. A specific zone ID restricts the tag to that zone. |
data-formats | Comma-separated list to enable only some formats on this page, for example display,native. Overrides property defaults. |
data-pop-cap | Popunder frequency per user in hours. Default 24. Cannot be set below 12. |
data-push-delay | Seconds before the push subscription prompt may appear. Default 15. Cannot be set below 5. The prompt is shown once per 7 days per user. |
data-sub1 to data-sub5 | Sub-IDs for your own reporting, available in statistics with group_by=sub1. |
data-consent | tcf (default: read IAB TCF signal if present), granted, or denied. Use granted or denied if you manage consent without a TCF-compatible CMP. |
data-lazy | true defers display zones until they are within 200 px of the viewport. Improves viewability and page speed. |
For display placements, add one <ins class="d4-ad"> element per zone where the ad should render. Pop, push, interstitial, and native widget zones need no placeholder. The tag sets only the identifiers listed in the Privacy Policy and honours Global Privacy Control.
15 Smartlink Parameters
A smartlink is a single URL that detects the visitor's geo, device, OS, browser, and language and routes to the best-paying eligible offer. Use it where you cannot place a tag: email, push lists, social bios, app traffic, or as a fallback.
https://link.d4rkw3b.network/PUB-7Q2K9M?zone=zn_01J9X5B2&sub1={your_subid}&vertical=igaming
| Parameter | Notes |
|---|---|
zone | Required. A zone of format smartlink or direct. |
sub1 to sub5 | Optional sub-IDs for reporting and for passing your own click identifier |
vertical | Optional. Restrict routing to one vertical. Defaults to all verticals the zone accepts. |
fallback | Optional URL-encoded URL to redirect to when no eligible offer exists for the visitor |
geo, device | Optional overrides when you already know the visitor's attributes (for example from an email list). Mismatches with detected values are logged and may be excluded. |
ip, ua | For server-side redirects only: pass the end user's IP and user agent so routing and fraud scoring use the visitor's values rather than your server's. Requires the request to come from an IP registered in Settings. |
Smartlinks pay on an RPM basis: revenue is attributed per click based on the conversions the routed offers generate, and shown in statistics with group_by=zone,sub1.
16 Webhooks
Register an HTTPS endpoint under Settings → Webhooks to receive events as they happen. Each delivery is a POST with a JSON body and is retried with exponential backoff for up to 24 hours until your endpoint returns a 2xx.
| Event | Sent when |
|---|---|
campaign.status_changed | A campaign moves between draft, review, active, paused, rejected, or completed |
creative.reviewed | A creative is approved or rejected, with reviewer notes |
campaign.budget_reached | Daily or total budget is exhausted |
balance.low | Advertiser balance falls below 24 hours of spend at the current rate |
conversion.received | A postback is accepted (optional; high volume) |
zone.status_changed | A zone moves between pending, audit, active, paused, or rejected |
zone.quality_alert | A zone's traffic quality score falls below threshold, with 48-hour remediation notice |
payout.scheduled | A payout is queued with its amount and date |
payout.sent | A payout is sent, with the reference or on-chain hash |
compliance.action | Any enforcement action on the account, with reference number and appeal deadline |
POST /your/endpoint
Content-Type: application/json
X-D4-Event: creative.reviewed
X-D4-Delivery: dlv_01J9X6C3R7
X-D4-Signature: sha256=4f1a0c7e9b2d8a6f3e5c1b9d7a2f4e6c8b0d2a4f6e8c0b2d4a6f8e0c2b4d6a8f
{
"event": "creative.reviewed",
"occurred_at": "2026-10-22T09:14:03Z",
"data": {
"creative_id": "crt_01J9X5D4",
"campaign_id": "cmp_01J9X4M2",
"status": "rejected",
"reason": "Landing page bonus terms do not state wagering requirement (AUP 4.1)"
}
}
Verify every delivery: compute HMAC-SHA256 of the raw request body using your webhook secret and compare it in constant time to the X-D4-Signature value. Reject deliveries whose occurred_at is more than 5 minutes old to prevent replay. The X-D4-Delivery ID is unique per attempt and can be used for idempotency.
17 OpenRTB Endpoint
Demand-side platforms and large advertisers can buy inventory programmatically over OpenRTB 2.6 at https://rtb.d4rkw3b.network/bid. Access is granted after an integration review; request it through your account manager or api@d4rkw3b.network.
- Supported: banner, native (OpenRTB Native 1.2), video (VAST 4.x), and a
popextension for popunder inventory. - Bid requests include
bcatandbattr, the publisher's accepted vertical list inext.d4.verticals, andext.d4.adultindicating adult-flagged inventory. Bidders must respect these fields; responses that violate them are discarded and counted against the integration's error budget. - Bid requests carry the IAB TCF string in
user.ext.consentand GPP inregs.ext.gppwhen available. - Timeout is 120 ms. Responses over timeout are discarded. Bidders sustaining over 2% timeouts are throttled.
- Creatives bid through RTB are subject to the same compliance review as dashboard creatives. Pre-register creatives via
POST /rtb/creativesto avoid first-serve delays. - Billing is on nurl or burl with the standard
${AUCTION_PRICE}macro. Invoicing is weekly against a prepaid balance.
18 Code Examples
Create and activate a campaign
curl -X POST https://api.d4rkw3b.network/v1/campaigns \
-H "Authorization: Bearer $D4_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "VPN US desktop pop",
"vertical": "vpn",
"format": "pop",
"pricing_model": "cpm",
"bid": "1.40",
"daily_budget": "200.00",
"landing_url": "https://example-vpn.com/?cid={click_id}&z={zone_id}",
"targeting": { "geo": ["US"], "device": ["desktop"] }
}'
curl -X POST https://api.d4rkw3b.network/v1/campaigns/cmp_01J9X4M2/activate \
-H "Authorization: Bearer $D4_API_KEY"
Pull yesterday's stats by zone
import os, requests from datetime import date, timedelta yesterday = (date.today() - timedelta(days=1)).isoformat() r = requests.get( "https://api.d4rkw3b.network/v1/stats", headers={"Authorization": f"Bearer {os.environ['D4_API_KEY']}"}, params={"from": yesterday, "to": yesterday, "group_by": "zone", "metrics": "impressions,clicks,conversions,spend"}, timeout=30, ) r.raise_for_status() for row in r.json()["data"]: print(row["zone"], row["clicks"], row["conversions"], row["spend"])
Verify a webhook signature
const crypto = require('crypto'); function verify(rawBody, signatureHeader, secret) { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex'); return expected.length === signatureHeader.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader)); }
Send a conversion postback
$params = http_build_query([ 'click_id' => $_SESSION['d4_click_id'], 'event' => 'deposit', 'payout' => '45.00', 'txid' => $depositId, ]); $sig = hash_hmac('sha256', $params, $postbackSecret); file_get_contents("https://pb.d4rkw3b.network/conv?{$params}&sig={$sig}");
19 Support
Integration questions: api@d4rkw3b.network. Include your account ID, the endpoint, the request_id from any error, and timestamps in UTC. Responses within one business day; managed-service accounts can reach their account manager on Telegram for same-hour help. Service health is published on the status page, and the unauthenticated endpoint GET /v1/status returns the same data as JSON for monitoring.