Everything you need to embed a content locker, publish a smart link, launch an offerwall and pull our offer feed into your own front end. Copy-paste snippets, real endpoints, real response payloads.
/api prefix
on your main domain. The exact URLs for your account are printed on your app’s API
documentation page in the dashboard: prefer copying them from there over hardcoding.
Three steps from sign-up to your first paid conversion. Everything after that is configuration.
Register as a publisher and confirm your email. Your dashboard opens with a balance, statistics and the tools list.
Create a Content Locker, Smart Link or Offerwall app.
Paste the snippet, share the link, or call the feed from your backend. Conversions credit your balance automatically.
401 Unauthorized and never disclose whether an App ID exists. The locker
embed and the smart link keep working regardless; they need no key.
A locker gates any part of your page (a download, a video, an article, a coupon) behind one completed offer. Integration is a single script tag; the loader handles the overlay, the offer list, the click and the unlock poll for you.
Your script tag runs on page load.
Overlay, fullpage or inline, per load method.
Filtered by the visitor’s country and device.
We log it and hop the visitor to the advertiser.
The postback releases the content and credits you.
Every locker exposes its own loader URL, built from its public code. Copy the exact line from the locker’s embed page.
<script src="https://cdn.toroads.com/cl/js/{locker_id}"></script>
The load method decides who starts the opening, and the load delay decides how long after that. They are not two knobs on one timer.
| Template | Load method | What happens | Delay applies? |
|---|---|---|---|
| Overlay / Fullpage | on_load | Opens at DOM ready plus the delay. | Yes |
| Overlay / Fullpage | javascript | Nothing opens by itself; the script prewarms a hidden frame and waits for your call. | No |
| Inline | n/a | Mounts into your container at DOM ready plus the delay. | Yes |
javascript method your timer is the only clock. A
setTimeout(..., 7000) on a locker configured with a 3s delay fires at 7000ms: the
two do not add up and the platform value does not win.
Each locker gets its own global function, derived from its public id; it is not a shared
toro_load(). The embed page prints the real name; here it is vd7.
The panel gives you the four snippets below and they are the whole set: pick one, paste it
once, change the number if it has one.
<script>vd7();</script>
<script>setTimeout(function() { vd7(); }, 5000);</script>
<button onclick="vd7()">Unlock Content</button>
<button onclick="setTimeout(function() { vd7(); }, 5000)">Unlock Content</button>
For an inline locker there is no trigger question: the container is the trigger. Put it where the locker should render.
<div id="captcha-locker"></div>
<script src="https://cdn.toroads.com/cl/js/{locker_id}"></script>
A link is the lightest integration we have: no script, no key, nothing to embed. You share a URL, we pick the best-paying eligible offer for that visitor’s country and device, record the click, and redirect. Both prefixes below hit the same resolver, so links already in circulation keep working.
/sl/{uuid}Global rotator. Each visitor is routed to the highest-EPC offer matching their GEO and device at that moment.
/dl/{uuid}Pinned to one offer. Same tracking and fraud checks, no rotation: use it when you promote a specific campaign.
/sl/{uuid}
https://your-domain.com/sl/{link_id}
https://your-domain.com/dl/{link_id}
Direct Links are templates you fill in yourself. They carry a single {user_id} macro:
substitute your end user’s identifier before you publish the link, and that value comes back to
you on the conversion postback.
# template as shown in the dashboard
https://your-domain.com/dl/{link_id}/{user_id}
# what you publish for user 4821
https://your-domain.com/dl/{link_id}/4821
A hosted, brandable wall of offers for your app or site, denominated in your own virtual currency.
There is nothing to sign and nothing to compute. The only URL you need is the one on
your app’s page in the dashboard; your api_secret is not involved here, it is used
only to verify the postbacks we send you.
<iframe
src="https://titan.toroads.com/123/end-user-42"
style="border:0;width:100%;height:720px"
title="Offers"></iframe>
Substitute your App ID for 123 and your own end-user id for end-user-42. You can also simply link to that URL instead of framing it.
url is an opaque, encrypted link generated by the platform.
Pass it through exactly as you received it and never construct one by hand.
Your lockers and links are served from a host you choose. There are two kinds and you can
have both: a free subdomain we generate for you in one click, and your
own custom domain. Both are managed in one place:
Custom Domains, reached from the button in the header of the Content Locker
or Smart & Direct Link tool, or at /tools/custom-domains.
| Free subdomain | Your own domain | |
|---|---|---|
| Setup | One click, nothing to configure | One DNS record at your registrar |
| Ready | Immediately | Once DNS propagates: minutes, sometimes an hour |
| HTTPS | Included | Issued automatically, nothing to renew |
| Visitor sees | A short random label on one of our edge domains | Your own hostname |
| How many | Five per account by default | No fixed limit |
Under Free Subdomains, press Generate. You get a short, random hostname served from our edge with HTTPS already working, and it is yours for good: the label is never rotated or re-pointed once issued.
One of them is the default, used whenever a locker or link does not name a host itself. You can disable any of them, and whatever was being served from it stops until you switch it back on.
Use a subdomain, not your apex. Your main site stays untouched and you can point the subdomain away again at any time.
go.yoursite.com
unlock.yoursite.com
links.yoursite.com
Add the hostname and it starts pending. The panel then prints the exact DNS record for your domain: copy it from there rather than from any guide, including this page. Create that record at your registrar, press refresh, and the certificate is issued for you. When the domain flips to connected, new lockers and links pick it up, and anything you already published on the old hostname keeps working.
Each domain carries two switches, CL and SL, for whether Content
Locker and Smart Link traffic may use it, so one hostname can be kept for one product. A
platform-owned hostname cannot be connected, and a hostname belongs to one account at a
time, which is what stops anyone else serving their content on your domain.
The embed panel has a domain picker listing your own domains and your free subdomains, and the host in the snippet is whichever you picked, so choose before you copy. With neither connected there is nothing to build a snippet against, and the panel asks you to connect a domain first.
Returns the offers eligible for an app, already filtered by country, device and the app’s own
configuration, so you can render offers in your own UI. All endpoints return
application/json.
Offer feed (v1 / v2 / v3): https://api.toroads.com
Everything else: https://your-domain.com/api
The /api prefix is not part of the path on the feed domain. The click
redirects and offerwall endpoints keep the prefix on your main domain.
| Scheme | Used by | How |
|---|---|---|
| App API key | Offer Feed | Send Authorization: Bearer <APP_API_KEY> or X-Api-Key: <APP_API_KEY>. The legacy ?api= query parameter still works but is deprecated: a key in a query string is written into every access log and sent onward in Referer. |
| None | Offerwall & redirect helpers | Opened by an end user’s browser; they take no credential. |
| Version | Endpoint | Returns |
|---|---|---|
| v1 | GET /v1/offers/{id}/{user_id} | All offers (verbose) |
| v2 | GET /v2/offers/{id}/{user_id} | Offerwall offers (clean) |
| v3 | GET /v3/offers/{id} | Content-locker offers (clean) |
Wrong App ID (HTTP 403).
| Parameter | Required | Description |
|---|---|---|
api | Yes | The app’s API key (or send it as a header, preferred). |
ip | No | End-user IP. When supplied, the country is resolved from it. |
country_code | No | ISO-3166 alpha-2, e.g. US. Send all to force no country filter. |
platform | No | ios, android, desktop / web. |
offer_type | No | CPI, CPA, PIN, CPE, CPR, Survey, other. |
v3 returns at most 10 offers, boosted and premium inventory first. A parameter you leave out can be
answered from the request itself, but only when the request comes from a browser,
detected from User-Agent and Accept.
A visitor’s browser calls v3 directly
Not supported for production. A browser call carries your API key where the visitor can read it. Keep the key on your server.
curl, Guzzle, requests, axios, Postman
The supported path. Your datacenter is not your visitor, so nothing is inferred: pass ip and platform yourself.
| Request | Country | Device |
|---|---|---|
| Browser, no parameters | Visitor’s own country | Visitor’s own device |
Browser, ?platform=ios | Visitor’s own country | ios |
Browser, ?country_code=GB | GB | Visitor’s own device |
| Server (curl, Guzzle, …) | all | No device filter |
Your backend is deliberately excluded: your server’s location describes your infrastructure, not
your visitor. Always pass ip (or country_code) and
platform explicitly from your server.
Cache-Control: private, no-store, max-age=0.
curl -H "Authorization: Bearer APP_API_KEY" \
"https://api.toroads.com/v2/offers/123/end-user-42?country_code=US&platform=android"
200 OK{
"offers": [
{
"id": 1234,
"name": "Play Game X: reach level 10",
"description": "Install and reach level 10 within 7 days.",
"image": "https://cdn.example.com/offers/1234.png",
"offer_type": "CPI",
"payout": "2.40",
"countries": ["US", "CA", "GB"],
"devices": ["android", "ios"],
"url": "https://your-domain.com/api/redirect/eyJpdiI6Ii4uLiJ9"
}
]
}
payout is a string with two decimals, already adjusted for the app’s custom rate.devices is always non-empty, drawn from android, ios, web. An unrestricted offer lists all three rather than an empty array, so there is no empty case to branch on, and the values are exactly the tokens platform accepts.url is a ToroAds tracking link: send the user there and we record the click, then redirect to the offer.points, is_game, incent and events. v3 omits those.events is the goal ladder and is present only on offers with more than one goal. Branch on the key being absent, not on an empty array.Authentication and app-state failures answer with { "error": "<reason>" }.
| Code | Meaning |
|---|---|
| 200 | Success. |
| 401 | Authentication failed. |
| 403 | This endpoint does not serve that kind of app. Check the App ID against the version table. |
| 404 | Locker, offer or token not found. |
| 429 | Rate limited: retry_after seconds in the body. |
| 500 | Offer-feed app/auth error (see error) or an unhandled server error. |
Every failure carries a short error string describing what to fix. If a feed call
is refused and the reason is not obvious from your own configuration, contact support with
the App ID and the time of the call.
Keep reading: