AFC LogoAfrican Free Fire Community
Login

Partner Data API

How to authenticate, what each endpoint returns, and what to do when a call fails.

Connect

Base URL

https://api.africanfreefirecommunity.com/api/v1/partner/

Auth header

X-API-Key: afcp_3f9a_...

Your first call. Replace the key with the one AFC issued you, and run it from your server, never from a browser.

curl -H "X-API-Key: afcp_3f9a_YOUR_SECRET_HERE" \
  "https://api.africanfreefirecommunity.com/api/v1/partner/events/"
bash
What you get

A read-only REST API over the tournament data AFC has published to partners, for the events your organisation has been granted. An AFC admin publishes an event explicitly, normally once its results are final, so what you read is settled data rather than a match in progress. There is no write, no delete, and no live match feed. Everything is versioned under /api/v1/, so a future breaking change ships as /api/v2/ and leaves your integration alone.

ResourceWhat it is
eventsThe event card: name, slug, dates, tier, status, prize pool.
stagesThe structure of an event, with each stage's groups nested.
matchesPer-match rows: match number, map, MVP, whether the result is in.
standingsThe final ranked table for an event.
teamsEvery registered team, with event-wide totals and rosters.
playersEveryone who recorded stats, with their per-event stats.
designsBranded leaderboard templates: background art, placed logos, brand colours.

What the API never returns, on any plan: real names, emails, Discord IDs or any other personal data; room IDs and room passwords; AFC's internal database IDs; events AFC has not published to partners; site-wide rankings and tier ladders.

Events are always addressed by their slug, for example dynasty-cup-nigeria, never by a numeric ID. Store the slug: it is the only stable handle you are given.

Authenticating

Every request carries your key in the X-API-Key header. A key has three parts joined by underscores: the namespace afcp, a short public prefix you can safely quote in a support message, and the secret, which must never leave your servers.

A complete authenticated request.

curl -H "X-API-Key: afcp_3f9a_YOUR_SECRET_HERE" \
  "https://api.africanfreefirecommunity.com/api/v1/partner/events/"
bash

Your key is shown to the AFC admin once, at the moment it is created, and is stored only as a hash. Nobody at AFC can retrieve it afterwards. If you lose it, ask for a new one. If you think it has leaked, tell AFC immediately and they will revoke it.

A key can be revoked, deleted, or given an expiry date at issue time. An expiry date means the key works through the end of that day, UTC, which is what makes a key for a single tournament weekend practical. A revoked, deleted or expired key stops working instantly, as does every key belonging to a partner account AFC has suspended. All of those return 401. Send the key over HTTPS only, and never put it in client-side code, a mobile app, or a public repository.

If you present the correct key, the 401 body names the cause, for example Key expired. or Partner suspended., so you can tell an expiry apart from a suspension without asking. If the key itself is wrong, every failure reads Unknown or revoked key. instead. That is deliberate: a stranger guessing at keys must not be able to learn which ones exist.

Why a field or endpoint is missing

Your access has three layers, all set by AFC and all closed by default. If something you expected is not in the response, one of these three is the reason, and none of them is a bug.

First, the event has to be published to partners. An AFC admin publishes an event explicitly, and until they do, no partner can read it however broadly scoped. An unpublished event is a 404.

Second, resource toggles decide which endpoints answer at all: events, stages, matches, standings, teams, players, designs. If one is off for you, that endpoint returns 403 resource_not_enabled and the rest keep working.

Third, field toggles decide which fields appear inside a resource you can already read.

FieldAppears when AFC enables
placementPlacements. The team's best finish, on teams and standings.
killsKills, on teams, players and standings.
damageDamage, on teams, players and standings.
assistsAssists, on teams, players and standings.
rosterRosters. The player list on each team.
maps, mapMaps played. maps on a group, map on a match.
prize_poolPrize pool, on events.
mvpMVP, on matches. The in-game handle, or null if none was recorded.
banner_url, logo_url, esports_image_url, ...Images and files. Event banners and rules files, team logos, player esport images, and design artwork.
rules_text, descriptionDescriptions and rules text. The event rules blurb and a team's self-description.

A field you are not entitled to is absent from the JSON, not present and null. Read defensively: check that a key exists rather than assuming it does. A key that is present and null means the field is enabled for you and the underlying value is genuinely empty, for example a team that never uploaded a logo.

Endpoints

Eight endpoints, all GET. Any other verb returns 405. Paths below are relative to the base URL, and the slug placeholder in each path is an event slug returned by the events list. Every list endpoint is paginated.

EndpointToggleReturns
GET events/eventsThe events you may read, newest first.
GET events/{slug}/eventsOne event. 404 if it is not yours to read.
GET events/{slug}/stages/stagesStages in running order, each with its groups nested.
GET events/{slug}/matches/matchesThe event's matches. Room credentials are never included.
GET events/{slug}/standings/standingsThe final ranked table, winners first.
GET events/{slug}/teams/teamsEvery registered team with its event-wide totals.
GET events/{slug}/players/playersEveryone who recorded stats, scoped to this event.
GET events/{slug}/designs/designsBranded leaderboard templates for the event's owner.

GET events/

Returns the standard envelope. is_native_afc tells you whether AFC ran the event itself or an organiser did, without revealing which organiser.

{
  "results": [
    {
      "slug": "dynasty-cup-nigeria",
      "name": "DYNASTY CUP NIGERIA",
      "competition_type": "tournament",
      "participant_type": "squad",
      "tier": "tier_3",
      "status": "completed",
      "start_date": "2026-06-29",
      "end_date": "2026-07-31",
      "is_native_afc": true,
      "banner_url": "https://api.africanfreefirecommunity.com/media/event_banner/DYNASTY_CUP_POSTER.png",
      "rules_file_url": "https://api.africanfreefirecommunity.com/media/event_rules/AFC_RULESET.pdf",
      "rules_text": null
    }
  ],
  "has_more": false,
  "next_offset": null,
  "total_count": 1
}
json

GET events/<slug>/stages/

Nests each stage's groups. order is a 1-based sequence number within the event, not a database ID.

{
  "stage_name": "Grand Final",
  "order": 3,
  "format": "br - normal",
  "status": "completed",
  "start_date": "2026-06-28",
  "end_date": "2026-06-28",
  "groups": [
    { "group_name": "Group A", "playing_date": "2026-06-28", "maps": ["bermuda"] }
  ]
}
json

GET events/<slug>/matches/

One row per match.

{
  "match_number": 1,
  "result_inputted": true,
  "map": "bermuda",
  "mvp": "ASN REAPER"
}
json

GET events/<slug>/standings/

The final table, rank ascending.

{
  "results": [
    { "rank": 1, "team": "SOLAR FLARE ESPORT", "placement": 1, "kills": 148 },
    { "rank": 2, "team": "BERSERK GENERATION", "placement": 1, "kills": 122 },
    { "rank": 3, "team": "V-ENT ESPORTS", "placement": 1, "kills": 113 }
  ],
  "has_more": true,
  "next_offset": 3,
  "total_count": 37
}
json

Ranking uses the same metric as AFC's official standings: placement points plus kill points plus bonus points minus penalty points, with first-place finishes and then total kills breaking ties. placement is the team's best finish across the event. Solo events are ranked by player, and each row carries username and in_game_id instead of team.

GET events/<slug>/teams/

Every registered team sorted by name, with its event-wide totals.

{
  "team": "ALLSTARS NG",
  "team_tag": "ASN",
  "status": "played",
  "logo_url": "https://api.africanfreefirecommunity.com/media/teams_logos/asn.jpg",
  "description": "We grind every night.",
  "placement": 1,
  "kills": 44,
  "roster": [
    { "username": "ASN GABBY", "in_game_id": "3098864559", "kills": 3 }
  ]
}
json

This list is the full registration list, not the list of competitors. Registering for an AFC event and playing in one are different things, and a team that registered but never played returns zeroed stats and an empty roster, which is easy to mistake for a team that played badly. Read status to tell them apart before you draw a bracket, a standings card, or a team count. status is always present and does not depend on any toggle.

ValueMeaning
playedTurned up and played at least one match. These are the competitors.
registeredAccepted into the event, has not played a match.
waitlistedSigned up but holding a waitlist slot, not a playing slot.
pendingRegistration submitted and awaiting approval. Sponsored events only.
no_showWas expected to play and the organizer marked it absent.
withdrawnPulled out of the event.
leftLeft the event.
disqualifiedRemoved by the organizer for a rules breach.

Only one value is returned per team. Where more than one could apply, the more specific one wins, in this order: disqualified, withdrawn, left, pending, waitlisted, no_show, played, registered. So a team that played two maps and then withdrew comes back as withdrawn, not played, and its stats are a partial record of an event it did not finish. Note also that a team can hold match rows without being played: AFC seeds teams into a map before it is contested, and a team that does not turn up keeps that row with no result.

GET events/<slug>/players/

Scopes every stat to this event rather than to career totals. esports_image_url is the player's posed roster photo, for lower-thirds and versus cards, and is null for players who have not uploaded one.

{
  "username": "Ak REBORN",
  "in_game_id": "7171703030",
  "esports_image_url": "https://api.africanfreefirecommunity.com/media/esports_pictures/reborn.jpg",
  "kills": 2
}
json

GET events/<slug>/designs/

The branded templates behind this event's graphics, so you can produce on-brand standings cards yourself. The Designs toggle unlocks the endpoint; the artwork additionally needs Images and files.

{
  "name": "DYNASTY CUP",
  "design_type": "leaderboard",
  "text_color": "#FFFFFF",
  "accent_color": "#34d27b",
  "transparent_background": false,
  "max_rows": 18,
  "is_default": true,
  "background_instagram_url": "https://api.africanfreefirecommunity.com/media/org_leaderboard_designs/DYNASTY_IG.png",
  "background_youtube_url": "https://api.africanfreefirecommunity.com/media/org_leaderboard_designs/DYNASTY_YT.png",
  "logos": [
    { "image_url": "https://api.africanfreefirecommunity.com/media/org_leaderboard_logos/sponsor.png",
      "x_pct": 12.5, "y_pct": 8.0, "size": "medium" }
  ]
}
json

The two canvases are Instagram portrait at 1080x1350 and YouTube landscape at 1920x1080. Logo positions are a percentage of the canvas anchored at the logo's centre, so the same pair of numbers places a logo correctly on both sizes. max_rows is how many standings rows the template is designed to hold. You only ever receive designs belonging to the event's owner: the organiser's library for an organiser-run event, or AFC's own library for a native AFC event.

Rate limits

Requests are limited per key, in a fixed one-minute wall-clock window. Your ceiling is 60 requests per minute unless AFC agreed a different figure for your key.

Every successful response tells you where you stand, so you can slow down before you are blocked.

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
http

Exceed the limit and the next request is refused until the window rolls over.

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{ "error": "rate_limit_exceeded" }
http

Watch X-RateLimit-Remaining and pause as it approaches zero, or honour Retry-After after a 429. Because the window is a wall-clock minute, a fresh allowance begins when the minute rolls over rather than sixty seconds after your first call.

Errors

Errors raised by the API carry a single error field alongside the matching HTTP status. Switch on the status code rather than on the body: a wrong HTTP verb is refused by the web framework before the API sees it and carries a detail field instead, and a request to a path that does not exist at all is not JSON.

{ "error": "not_found" }
json
StatusBodyWhat it means and what to do
401Unknown or revoked key.Your key is missing, malformed, unknown, revoked or expired, or your partner account is suspended. Check the header first, then contact AFC.
403resource_not_enabledYou are authenticated, but this resource is switched off for your account. Ask AFC to enable it.
404not_foundYou cannot read that event. Confirm the slug with AFC.
405detail, not errorEvery endpoint is GET only. This one is refused before the API sees it, so its body carries a detail field rather than error.
429rate_limit_exceededYou are over your per-minute budget. Honour the Retry-After header.

The difference between 403 and 404 is deliberate. A 403 says the resource type is switched off for you. A 404 says you cannot see that event, and it is returned whether the event is out of your scope, not yet published, or simply does not exist. The API will not confirm that an event exists if you are not allowed to read it, so a 404 is never proof that a slug is wrong.

Images, logos and artwork

Every image and file URL the API returns is absolute and publicly fetchable, so you can download it or hot-link it directly. They are plain URLs with no signature and no expiry, which means they keep working, and also means anyone you pass one to can fetch it. Treat a media URL as public, because it is.

A media field, as returned.

"logo_url": "https://api.africanfreefirecommunity.com/media/teams_logos/asn.jpg"
json

A missing asset is null, never an error. A team with no uploaded logo returns logo_url as null. The field is only absent entirely when the Images and files toggle is off for your account.

Download media once and serve it from your own storage rather than hot-linking it on every page view. Banners, logos and design artwork change rarely, your pages will render faster, and you will not be affected if AFC moves an asset. Media responses carry ETag and Last-Modified, so if you would rather revalidate than re-download, a conditional request with If-None-Match gets you a cheap 304.

Working with the API

Poll, do not hammer. Data only changes when AFC publishes or corrects a result. Once a day during a season, or once an hour around a final, is plenty. There are no webhooks.

Handle absent fields. A field you are not entitled to is missing from the JSON, so read defensively rather than assuming a key exists.

Expect a 404 to be lasting. If an event you were reading starts returning 404, AFC has either unpublished it or changed your scope. Retrying will not help, so ask AFC.

Slugs are the identifiers. Store the event slug, not the position of an event in a list, because a new event changes every position.

Contact your AFC partner manager for a new or rotated key, to change which events or resources you can read, or to report anything the API returns that looks wrong. Include the endpoint, the timestamp, and your key's prefix, the afcp_3f9a part, never the full key.