Offers API
The Offers API returns our offer catalogue as JSON so you can render it inside your own interface instead of using the hosted offerwall.
API access is granted separately from placement approval. Your placement needs
request_api = approved before any of these endpoints return data. Request access from
your placement settings.
Two modes, one endpoint
The feed serves two integration shapes. Which one you get depends on whether you include the
[USER_ID] path segment — nothing else.
| Mode | Request | Use it for |
|---|---|---|
| Publisher | /api/v1/offers/[APP_ID]/[USER_ID] |
Serving a feed to one of your signed-in users. |
| Partner | /api/v1/offers/[APP_ID] |
Importing the catalogue into an external CPA network. |
Targeting, ranking, filtering and pagination are identical in both modes. Only two things differ:
| Publisher | Partner | |
|---|---|---|
reward_name, currency |
Returned | Omitted |
points, reward, usd_jmpt |
Returned | Omitted |
payout (advertiser USD) |
Returned | Returned |
Tracking url |
/api/redirect/[TOKEN] |
/api/redirect/[TOKEN]?subid={SUBID} |
| Default country targeting | Resolved server-side | Whole catalogue |
Partner mode returns no reward or currency data — a network has no relationship with a
publisher's virtual currency. You receive payout, the advertiser payout in USD, which is
what your own economics are based on.
It also defaults to the entire catalogue. You import once and target your own users
later, so resolving your server's country would hand you the wrong subset. Pass ip or
country_code if you do want it narrowed.
Authentication
Send your API key either as an Authorization: Bearer header or as the api
query parameter. Both are accepted on both modes.
An API key authorises only the App Placement it was issued for. A key for one placement will not open another, whichever transport you use.
# Publisher mode - header auth
curl -H "Authorization: Bearer ak_9f2c7e1b4d8a35660c1ee9b7a4f0d213" \n "http://adplusmedia.com/api/v1/offers/1042/user_8837"
# Partner mode - query auth, no user segment
curl "http://adplusmedia.com/api/v1/offers/1042?api=ak_9f2c7e1b4d8a35660c1ee9b7a4f0d213"
Call this endpoint from your server, not from the browser. A client-side request exposes your API key and leaks your full offer catalogue and payouts to anyone inspecting network traffic.
Query parameters
| Parameter | Required | Description | Default |
|---|---|---|---|
api |
Yes* | Your placement API key. *Or send it as an Authorization: Bearer header instead. |
— |
country_code |
No | Two-letter country code used to filter offers by targeting. Pass all to disable country targeting and receive the whole catalogue. |
Publisher: resolved server-side. Partner: all |
ip |
No | The end user's IP address. When present, we resolve the country from it and country_code is ignored. |
— |
platform |
No | Restrict the feed to a single platform. Accepts ios, android, desktop or web. |
All platforms |
offer_type |
No | Restrict the feed to a single offer type. | All types |
page |
No | Page number to retrieve. | 1 |
per_page |
No | Offers per page. Capped at 200. |
50 |
macro |
No | Partner mode only. Overrides the subid macro, for example [USER_ID]. |
{SUBID} |
Filtered example
curl "http://adplusmedia.com/api/v1/offers/1042/user_8837?api=YOUR_API_KEY&country_code=US&platform=android"
Response
A successful call returns HTTP 200. The envelope depends on the mode.
Publisher mode
{
"offers": [
{
"id": 4821,
"name": "Monopoly GO!",
"description": "Reach board level 15 within 7 days.",
"image": "https://cdn.example.com/offers/4821.png",
"offer_type": "offer",
"payout": "1.96",
"points": 196,
"reward": 196,
"usd_jmpt": "0.9800",
"countries": ["US", "CA", "GB"],
"instructions": "Install the game and reach board level 15.",
"requirements": "New users only.",
"is_game": 1,
"incent": 1,
"url": "http://adplusmedia.com/api/redirect/eyJpdiI6IlZ4..."
}
],
"reward_name": "Coins",
"currency": { "name": "Coins", "icon": null },
"pagination": { "page": 1, "per_page": 50, "total": 137, "total_pages": 3, "has_more": true }
}
Partner mode
No reward_name, no currency, and no per-offer reward fields. The tracking
url carries the subid macro.
{
"offers": [
{
"id": 4821,
"name": "Monopoly GO!",
"description": "Reach board level 15 within 7 days.",
"image": "https://cdn.example.com/offers/4821.png",
"offer_type": "offer",
"payout": "1.96",
"countries": ["US", "CA", "GB"],
"devices": ["android", "iphone"],
"isPremium": false,
"instructions": "Install the game and reach board level 15.",
"requirements": "New users only.",
"is_game": 1,
"incent": 1,
"url": "http://adplusmedia.com/api/redirect/eyJpdiI6IlZ4...?subid={SUBID}"
}
],
"pagination": { "page": 1, "per_page": 50, "total": 137, "total_pages": 3, "has_more": true }
}
The feed is paginated. You receive 50 offers per page by default (maximum
200). Use page and per_page, and keep requesting until
pagination.has_more is false.
Offer object
| Field | Type | Mode | Description |
|---|---|---|---|
id | Integer | Both | Unique AdPlusMedia offer ID. |
name | String | Both | Offer title. |
description | String | Both | Short description of the required action. |
image | String | Both | Offer icon URL. |
offer_type | String | Both | Category of the offer, for example offer or survey. |
payout | String | Both | Advertiser payout in USD, formatted to two decimal places. |
countries | Array | Both | ISO alpha-2 codes the offer is available in. |
instructions | String | Both | Step-by-step completion instructions, when the offer defines them. |
requirements | String | Both | Eligibility requirements, when the offer defines them. |
events | Array | Both | Individual reward events for multi-step offers, when present. |
incent | Integer | Both | 1 when incentivised traffic is allowed. |
url | String | Both | Encrypted tracking URL. In partner mode it ends with ?subid={SUBID}. |
points | Number | Publisher | Reward in your virtual currency (payout × your rate). |
reward | Number | Publisher | Same value as points, kept for older integrations. |
usd_jmpt | String|null | Publisher | Payout converted to JumpTask currency. null when that service is unavailable. |
devices | Array | Partner | Supported devices, so you can run your own device targeting. |
isPremium | Boolean | Partner | true for featured inventory. |
devices and isPremium are returned in partner mode only. A
partner runs its own targeting, so it needs them; a publisher feed is already targeted server-side by
the time you receive it.
Sending users to an offer
Every offer carries a ready-to-use url. Open it and the platform records the click and
forwards the user to the advertiser. Fetching the feed never creates a click — only a real
visit to that URL does.
Publisher mode
The url is already bound to the [USER_ID] you requested the feed with. That
identifier is sealed inside the encrypted token, so it cannot be altered by anything appended to the URL.
Use it as-is.
If a subid parameter is present on a publisher link it is ignored entirely.
The encrypted identifier always wins — it is never overwritten, merged or compared.
Partner mode — the subid macro
A partner has no end-user identifier when the catalogue is imported, so every tracking URL ends with a macro you replace before sending traffic:
http://adplusmedia.com/api/redirect/eyJpdiI6IjRhTn...In0=?subid={SUBID}
^^^^^^^^
replace this with your own identifier
| Rule | Detail |
|---|---|
| What it holds | Your unique tracking identifier. It may be a user ID, click ID, transaction ID, session ID, wallet ID, UUID, or any other unique identifier generated by your platform. AdPlusMedia stores this value with the generated click and returns it in postbacks where appropriate. It is treated as opaque — we never parse or interpret it. |
| Maximum length | 255 characters after trimming. Longer values are rejected, not truncated. |
| Required | Yes. An empty, whitespace-only or missing subid is refused. |
| Custom macro |
Add ?macro=[USER_ID] to the feed request if your import tool needs a different
token — for example [USER_ID], {sub1} or {s1}.
|
You must substitute the macro before redirecting traffic. A click arriving with
{SUBID} or [SUBID] still in the URL returns an Integration
Error and no click is created.
This is deliberate. Accepting an unsubstituted macro would produce clicks that can never be attributed or paid, while still costing you traffic — and they would look perfectly healthy in every report. Failing on the first click is far cheaper than discovering it in a reconciliation weeks later.
Tracking URLs never expire. Import them once and keep them for as long as you like — they are refused only if the App Placement or the offer itself is disabled.
Building a link yourself
You can also construct a click URL directly from an offer's id and your user's identifier:
function offerClickUrl(offerId, userId) {
return 'http://adplusmedia.com/api/manual-offer/1042/'
+ encodeURIComponent(offerId) + '/'
+ encodeURIComponent(userId);
}
This endpoint accepts one extra optional parameter, source_id, which is stored against the
click and returned to you for your own sub-ID reporting.
Caching
The feed is not paginated and can be large. Cache it on your server for a few minutes rather than calling it on every page view.
The v1 endpoints compute usd_jmpt by calling an external currency service on
every single request, with no caching. If you do not need that field, use v2 — it is
meaningfully faster.
Errors
Every failure on these endpoints returns HTTP 500 with a JSON error field,
including client mistakes such as a bad API key. See
Errors & Responses for the full list.
{
"error": "Invalid API Key"
}