Skip to main content
D1 Arena

D1 Arena

Loading...

D1 Arena

Developer API

Community

Developer API

Build bots, overlays, stream tools, and integrations with D1Arena data.

Authentication

All API requests require an API key passed in the X-API-Key / Authorization: Bearer header.

# Example request curl -H "X-API-Key: d1_your_api_key_here" \ https://d1arena.com/api/v1/streams

To create an API key, go to Developer Settings in your dashboard. You can have up to 5 keys.

API requests are rate-limited per API key. When you exceed the limit, requests return 429 Too Many Requests with a Retry-After header.

Base URL

https://d1arena.com/api/v1

All endpoints return JSON. Paginated endpoints include a meta object with current_page, last_page, and total.

Streams

GET /streams
List currently live streams. Supports pagination and category filtering.
ParameterTypeDescription
category_idintegerFilter by game/category ID
limitintegerResults per page (default: 20)
pageintegerPage number
Response
{ "data": [ { "id": 42, "name": "ProGamer99", "user_slug": "progamer99", "profile_img": "profile/abc123.jpg", "stream_title": "Ranked Grind - Road to Champion", "stream_category_id": 5, "is_vertical_stream": false, "platform_tier": "pro" } ], "meta": { "current_page": 1, "last_page": 1, "total": 3 } }
GET /streams/{slug}
Get a single streamer's live status and stream details by username or slug.
Response
{ "data": { "user_id": 42, "name": "ProGamer99", "slug": "progamer99", "is_live": true, "stream_title": "Ranked Grind", "category_id": 5, "is_vertical": false, "platform_tier": "pro", "profile_img": "profile/abc123.jpg" } }

Categories

GET /categories
List all game categories. Supports search and pagination.
ParameterTypeDescription
searchstringFilter categories by name
limitintegerResults per page (default: 50)
Response
{ "data": [ { "id": 5, "name": "Call of Duty", "slug": "call-of-duty", "image": "categories/cod.png" } ], "meta": { "total": 24 } }

Users

GET /users/{slug}
Get a player's public profile and competitive stats by username or slug.
Response
{ "data": { "user": { "id": 42, "name": "ProGamer99", "user_slug": "progamer99", "profile_img": "profile/abc.jpg", "bio": "Competitive FPS player", "platform_tier": "pro", "is_live": "1", "stream_title": "Ranked", "gold": 3, "silver": 1, "bronze": 0 }, "stats": { "elo_rating": 1842, "total_tournaments": 27, "win_rate": 64.5, "total_earnings": 1250.00 } } }

Clips

GET /clips
List public clips. Supports filtering by streamer and category.
ParameterTypeDescription
streamer_idintegerFilter clips by streamer's user ID
category_idintegerFilter by game/category ID
limitintegerResults per page (default: 20)
Response
{ "data": [ { "id": 99, "streamer_id": 42, "stream_category_id": 5, "title": "Insane 1v4 clutch", "slug": "insane-1v4-clutch-abc", "duration": 28, "view_count": 412, "is_auto_clip": false, "created_at": "2026-03-15T18:30:00.000000Z", "streamer": { "id": 42, "name": "ProGamer99", "user_slug": "progamer99" } } ], "meta": { "total": 156 } }
GET /clips/{slug}
Get a single clip's details by slug.
Response
{ "data": { "id": 99, "title": "Insane 1v4 clutch", "slug": "insane-1v4-clutch-abc", "description": "Final round comeback", "duration": 28, "view_count": 412, "streamer": { "id": 42, "name": "ProGamer99" }, "creator": { "id": 55, "name": "ClipMaster" }, "stream_category": { "id": 5, "name": "Call of Duty" } } }

Tournaments

GET /tournaments
List tournaments. Supports filtering by status and league.
ParameterTypeDescription
statusstringFilter by status (e.g. open, in_progress, completed)
league_idintegerFilter by league ID
limitintegerResults per page (default: 20)
Response
{ "data": [ { "id": 15, "title": "Friday Night Frenzy", "tournament_type": "single_elimination", "status": "open", "registration_fee": "5.00", "no_player": 32, "team": 0, "category": { "id": 5, "name": "Call of Duty" }, "start_date": "2026-03-28T20:00:00.000000Z" } ], "meta": { "current_page": 1, "last_page": 2, "total": 24 } }
GET /tournaments/{id}
Get tournament details and participant count.
Response
{ "data": { "id": 15, "title": "Friday Night Frenzy", "tournament_type": "single_elimination", "status": "open", "registration_fee": "5.00", "no_player": 32, "team": 0 }, "meta": { "participant_count": 18 } }

ELO Rankings

GET /elo/leaderboard
Get the ELO-ranked leaderboard. Optionally filter by game category.
ParameterTypeDescription
category_idintegerFilter by game/category ID
limitintegerNumber of results (default: 50)
Response
{ "data": [ { "rank": 1, "user_id": 42, "name": "ProGamer99", "user_slug": "progamer99", "elo_rating": 2150, "wins": 45, "losses": 12, "win_rate": 78.9, "profile_img": "profile/abc123.jpg" } ], "meta": { "total": 312 } }

Leagues

GET /leagues
List leagues with optional status and category filters.
ParameterTypeDescription
statusstringFilter by league status
category_idintegerFilter by game/category ID
limitintegerResults per page (default: 20)
Response
{ "data": [ { "id": 3, "name": "Spring 2026 Pro League", "status": "active", "category": { "id": 5, "name": "Call of Duty" }, "total_participants": 48, "start_date": "2026-03-01", "end_date": "2026-05-31" } ], "meta": { "current_page": 1, "last_page": 1, "total": 6 } }
GET /leagues/{id}/standings
Get league standings (player rankings by points).
Response
{ "data": [ { "id": 1, "points": 2400, "wins": 12, "losses": 3, "user": { "id": 42, "name": "ProGamer99", "user_slug": "progamer99" } } ], "meta": { "total": 48 } }

Error Responses

All errors return a consistent JSON envelope. The error object always contains a machine-readable code and a human-readable message.

Error Response Format
{ "success": false, "error": { "code": "not_found", "message": "The requested resource could not be found." } }
Validation Error Format (422)
{ "success": false, "error": { "code": "validation_error", "message": "The given data was invalid.", "errors": { "category_id": ["The category id must be an integer."], "limit": ["The limit must not be greater than 100."] } } }

Status Codes

200
OK — Request succeeded. Response contains the requested data.
400
Bad Request — The request is malformed or missing required parameters. Check the error.message for details.
401
Unauthorized — Missing or invalid API key. Ensure you're passing a valid key in the X-API-Key header.
403
Forbidden — Your API key has been disabled or lacks permission for this resource. Check your Developer Settings.
404
Not Found — The requested resource does not exist. Verify the slug, ID, or endpoint path.
422
Validation Error — Request parameters failed validation. The error.errors object maps field names to their specific issues.
429
Rate Limited — Too many requests. The Retry-After header indicates how many seconds to wait before retrying.
500
Server Error — An unexpected error occurred on our end. If this persists, contact support.

Error Codes Reference

CodeHTTP StatusDescription
invalid_api_key401The API key is missing, malformed, or does not exist
api_key_disabled403The API key has been revoked or disabled
not_found404The requested resource could not be found
validation_error422One or more request parameters are invalid
rate_limited429Request rate limit exceeded for this API key
server_error500Internal server error — please retry or contact support

Rate Limits

API requests are rate-limited per API key. When you exceed the limit, requests return 429 Too Many Requests with a Retry-After header.

Limits by Tier

TierRequests / Minute (Default)Max Keys
Starter (Free)605
PRO605
Ultimate605
Partner605

Rate Limit Headers

API requests are rate-limited per API key. When you exceed the limit, requests return 429 Too Many Requests with a Retry-After header.

HeaderDescription
Retry-AfterSeconds to wait before retrying (only present on 429 responses)

Best Practices

Tips for staying within limits:
  • Cache responses locally — stream and tournament data doesn't change every second.
  • Subscribe to webhooks for real-time events instead of polling endpoints.
  • Batch requests where possible — use filter parameters to get exactly what you need in fewer calls.

Webhooks (EventSub)

Subscribe to real-time push notifications instead of polling. When an event occurs, D1Arena sends an HTTP POST to your callback URL with a JSON payload signed with HMAC-SHA256.

Setup

Create webhook subscriptions in Developer Settings. Each subscription requires:

  • Callback URL — A publicly accessible HTTPS endpoint on your server.
  • Events — One or more event types to subscribe to.

You'll receive a whsec_ signing secret upon creation. Store it securely — it's shown only once.

Payload Format

Every webhook delivery sends a JSON body with this structure:

{ "id": "evt_a1b2c3d4e5f6", "event": "stream.online", "created_at": "2026-03-23T14:30:00Z", "data": { // Event-specific fields (see examples below) } }

Headers

Each delivery includes the following headers for routing and verification:

HeaderDescription
Content-Typeapplication/json
X-D1Arena-EventEvent type (e.g. stream.online)
X-D1Arena-SignatureHMAC-SHA256 hex digest of the raw request body
X-D1Arena-Signature-VersionSigning-key format: v2 for current subscriptions or v1-hashed-secret for legacy subscriptions
X-D1Arena-Delivery-IdUnique delivery UUID — use for deduplication
X-D1Arena-TimestampUnix timestamp of when the event was sent

Verifying Signatures

Always verify the X-D1Arena-Signature header before processing a webhook. The signature is computed as HMAC-SHA256(raw_body, webhook_secret).

For v2 deliveries, use the whsec_ secret shown when the subscription was created. For a pre-upgrade v1-hashed-secret delivery, first compute SHA256(whsec_secret) from that original secret and use the resulting lowercase hex digest as the HMAC key. Recreate the subscription when practical to move to v2.

PHP
// Get the raw body and signature header $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_D1ARENA_SIGNATURE'] ?? ''; // Compute expected signature $expected = hash_hmac('sha256', $payload, $webhookSecret); // Constant-time comparison to prevent timing attacks if (!hash_equals($expected, $signature)) { http_response_code(401); exit('Invalid signature'); } $event = json_decode($payload, true);
Node.js
const crypto = require('crypto'); app.post('/webhook', (req, res) => { const payload = req.rawBody; // Ensure raw body is available const signature = req.headers['x-d1arena-signature']; const expected = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(payload) .digest('hex'); if (!crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(signature) )) { return res.status(401).send('Invalid signature'); } const event = JSON.parse(payload); // Process event... res.status(200).send('OK'); });
Python
import hmac, hashlib, json def handle_webhook(request): payload = request.body signature = request.headers.get('X-D1Arena-Signature', '') expected = hmac.new( WEBHOOK_SECRET.encode(), payload, hashlib.sha256 ).hexdigest() if not hmac.compare_digest(expected, signature): return HttpResponse(status=401) event = json.loads(payload) # Process event... return HttpResponse(status=200)

Available Events

EventDescription
stream.onlineA streamer went live
stream.offlineA streamer went offline
channel.followA user followed a channel
channel.subscribeNew supporter subscription on a channel
channel.tipA tip was sent to a streamer
tournament.startedA tournament match play has begun
tournament.endedA tournament has concluded
tournament.match.completedA match result was recorded
clip.createdA new clip was created from a live stream
overdrive.startedD1 Frenzy started on a channel
overdrive.level_upD1 Frenzy advanced to the next level
overdrive.endedD1 Frenzy completed or expired

Event Payload Examples

stream.online

{ "id": "evt_a1b2c3d4e5f6", "event": "stream.online", "created_at": "2026-03-23T14:30:00Z", "data": { "user_id": 42, "user_slug": "progamer99", "name": "ProGamer99", "stream_title": "Ranked Grind - Road to Champion", "category_id": 5, "category_name": "Call of Duty", "protocol": "RTMP", "started_at": "2026-03-23T14:30:00Z" } }

stream.offline

{ "id": "evt_f6e5d4c3b2a1", "event": "stream.offline", "created_at": "2026-03-23T17:45:00Z", "data": { "user_id": 42, "user_slug": "progamer99", "duration_seconds": 11700, "vod_id": 281 } }

channel.follow

{ "id": "evt_c1d2e3f4a5b6", "event": "channel.follow", "created_at": "2026-03-23T15:10:00Z", "data": { "follower_id": 88, "follower_slug": "newplayer", "followed_id": 42, "followed_slug": "progamer99" } }

channel.tip

{ "id": "evt_d1e2f3a4b5c6", "event": "channel.tip", "created_at": "2026-03-23T16:20:00Z", "data": { "streamer_id": 42, "streamer_slug": "progamer99", "tipper_id": 55, "tipper_slug": "clipmaster", "amount": "5.00", "currency": "USD", "message": "Great stream!" } }

tournament.match.completed

{ "id": "evt_e1f2a3b4c5d6", "event": "tournament.match.completed", "created_at": "2026-03-23T21:15:00Z", "data": { "tournament_id": 15, "tournament_title": "Friday Night Frenzy", "match_id": 204, "round": 2, "winner": { "id": 42, "slug": "progamer99", "name": "ProGamer99" }, "loser": { "id": 77, "slug": "rival_x", "name": "Rival_X" }, "score": "3-1" } }

clip.created

{ "id": "evt_b1c2d3e4f5a6", "event": "clip.created", "created_at": "2026-03-23T15:45:00Z", "data": { "clip_id": 99, "slug": "insane-1v4-clutch-abc", "title": "Insane 1v4 clutch", "duration": 28, "streamer_id": 42, "streamer_slug": "progamer99", "creator_id": 55, "creator_slug": "clipmaster", "category_id": 5 } }

Delivery & Retry Policy

AttemptDelayNotes
1st (initial)ImmediateSent within seconds of the event
2nd (retry)30 secondsIf first attempt fails or times out
3rd (retry)2 minutesExponential backoff
4th (final)10 minutesLast attempt before marking as failed
Important: Your endpoint must respond with a 2xx status within 10 seconds. Non-2xx responses or timeouts trigger a retry. After 10 consecutive failures, the subscription is automatically disabled. You'll receive an email notification and can re-enable it in Developer Settings.

Best Practices

  • Always verify signatures before processing payloads to prevent spoofed events.
  • Use the Delivery-Id for deduplication — retries send the same ID, so store processed IDs to avoid double-processing.
  • Respond quickly, process asynchronously — return 200 OK immediately and handle business logic in a background job.
  • Use HTTPS endpoints only — webhook URLs must use TLS. HTTP callbacks are rejected.
  • Handle unknown events gracefully — new event types may be added. Return 200 for unrecognized events rather than erroring.

D1 Frenzy

D1 Frenzy is triggered by rapid tips and subscriptions while a streamer is live. It progresses through 5 levels with increasing targets.

GET /api/overdrive/{streamerId}

Get the active D1 Frenzy for a streamer. Returns {"active": false} if none.

{ "active": true, "level": 2, "progress": 150, "target": 250, "progress_pct": 60.0, "total_contributions": 8, "total_contributors": 5, "expires_at": "2026-03-22T15:30:00+00:00" }

Level Targets

LevelPoints
1100
2250
3500
41,000
52,000

D1 Frenzy — Points: Tip $1 → 100; Subscription 500 × Tier. Duration: 5 Minutes; Cooldown: 30 Minutes.

Extension SDK

Build custom panel and overlay extensions that streamers can install on their channel pages. Extensions run in sandboxed iframes and communicate with the host page via postMessage.

Getting Started

  1. Create an API key in Developer Settings.
  2. Build your extension as a standalone HTML page hosted on your domain (HTTPS required).
  3. Submit it for review in the My Extensions section.
  4. Once approved, streamers can install it from the Extension Marketplace.

Extension Types

TypeLocationBehavior
panelBelow the stream playerVisible when stream is live. Full-width card, 300px default height.
overlayOver the video playerVisible when live. Position/size controlled by the streamer via the overlay positioning tool.

postMessage API

Your extension receives context data automatically when it loads. Implement these events:

Use the exact D1Arena parent origin for every message. The official SDK derives and validates this origin from the embedding page automatically.

Because extension iframes intentionally use an opaque sandbox origin, the D1Arena host authenticates the exact registered iframe window. Extension code must still authenticate the parent window and exact D1Arena origin before accepting context.

1. Signal readiness

var d1ParentOrigin = 'https://d1arena.com'; // Tell the host page your extension is ready for context window.parent.postMessage({ type: 'D1_EXT_READY' }, d1ParentOrigin);

2. Receive context

window.addEventListener('message', function(e) { if (e.source === window.parent && e.origin === d1ParentOrigin && e.data && e.data.type === 'D1_CONTEXT') { var ctx = e.data.payload; // ctx.channelId - Streamer's user ID // ctx.channelName - Streamer's display name // ctx.channelSlug - Streamer's URL slug // ctx.viewerId - Current viewer's ID (null if not logged in) // ctx.isLive - Whether the stream is currently live } });

3. Send actions (optional)

// Redirect the host page (e.g. for a "Storm" button) window.parent.postMessage({ type: 'D1_EXT_ACTION', action: 'storm', target: 'username-slug' }, d1ParentOrigin);

Permission Scopes

Declare what data your extension needs. Reviewers verify your code matches your declared permissions.

ScopeGrants Access To
read:streamStream status, title, category
read:viewersViewer count and list
read:chatChat messages (via Pusher channel)
read:clipsChannel clips via /api/clips/{slug}
read:tournamentsActive match info via /api/active-match/{id}
read:channelChannel profile, followers, schedule

Security Requirements

Extensions run in a sandboxed iframe with sandbox="allow-scripts". Your extension cannot access cookies, localStorage, or make authenticated requests to d1arena.com.
  • Public HTTPS required — Your iframe URL must use TLS and resolve only to public network addresses.
  • Human-readable source — No obfuscated or minified-only JavaScript. Reviewers must be able to read your code.
  • No external script loading unless declared in your submission. CDN libraries (jQuery, Chart.js, etc.) are fine.
  • No data exfiltration — Extensions must not send viewer data to third-party analytics or tracking services.
  • Content policy — No ads, NSFW content, cryptocurrency mining, or malicious behavior.

Review Process

StatusMeaning
pendingSubmitted, awaiting admin review (typically 1-3 business days).
approvedApproved and visible in the Extension Marketplace.
rejectedRejected with a reason. Fix issues and resubmit.
suspendedTemporarily removed for policy violation. Contact support.

Version Updates

To update an approved extension, delete the current version and submit a new one with an incremented version number. The new version goes through review again.

Changelog

Track API changes and new features. We follow semantic versioning and announce breaking changes at least 30 days in advance.

v1.0March 2026
  • Initial public API release with API key authentication.
  • Streams: List live streams, get streamer details by slug.
  • Categories: Search and list all game categories.
  • Users: Public profiles with competitive stats, ELO rating, and medal counts.
  • Clips: Browse and retrieve clip details with streamer/creator info.
  • Tournaments: List, filter by status/league, get participant counts.
  • ELO Leaderboard: Global and per-category ranked leaderboards.
  • Leagues: List leagues with standings and point breakdowns.
  • D1 Frenzy: Real-time Frenzy status for any streamer.
  • Webhooks (EventSub): 12 event types including stream, channel, tournament, clip, and Frenzy events.
  • Rate Limits

Need Help?

Questions about the API? Contact us.