Offerwall API

Fetch offers as JSON and build your own wall, when the iframe is not enough.

Check the iframe first. The offerwall iframe handles device and country detection, offer filtering, click tracking and fraud checks for you. Use the API only when you genuinely need your own interface — and remember you then own all of that.

Endpoints

Endpoint Purpose Credentials
GET /api/v1/offers Fetch the offer feed. apiKey + apiSecret
GET /api/track Register a click and redirect to the advertiser. apiKey, userId, offerId
POST /api/v1/fingerprint Record a device fingerprint. None

Full reference: API Access, Parameters, Responses, Fingerprinting.

A working integration needs three steps

1. Your server calls GET /api/v1/offers with your
   app credentials and the end user's ID

2. You render your own offer list from the returned
   "data" array

3. When the user picks one, send them to the "link"
   value for that offer, which carries your API key,
   their user ID and the offer ID
Do not build the tracking URL yourself. Step 3 must use the link the API returns for each offer. If you construct it yourself and omit the userId — or invent a parameter name — the click fails validation, nothing is recorded, and you earn nothing with no error shown to you.

Because your user ID is required for the click to register, a custom wall has no way to display offers anonymously. Obtain the user ID before you fetch the feed.

Example

// Server side. Keep apiSecret out of any client-side code.
$response = Http::get('https://preads.hansaldev.com/api/v1/offers', [
    'apiKey'    => config('preads.api_key'),
    'apiSecret' => config('preads.api_secret'),
    'userId'    => $request->user()->preads_id,
    'limit'     => 20,
]);

if ($response->successful()) {
    foreach ($response->json('data') as $offer) {
        // $offer['payout']        -> your earnings, USD
        // $offer['payout_local']  -> what to show the user
        // $offer['link']          -> where to send them
        echo $offer['name'].' worth '.$offer['payout_local'].' '.$offer['currency'];
    }
}

Rates apply: 60 requests per minute. Cache the response rather than fetching it on every page view.

What you take on

The iframe handles these for you. With the API they become your responsibility:

  • Country and device filtering. Omit country and platform and the API filters on what your server looks like from, not what the user's browser reports — which can differ. Pass them explicitly if you know them.
  • Fraud fingerprinting. Send fingerprint on the tracking request if you collect one, or you will see more false rejections than the iframe does.
  • Rate limiting and caching.
  • Responsive rendering across desktop and mobile.

If any of that is more work than you want, use the iframe.

Troubleshooting

Symptom Cause and fix
401 INVALID_API_KEY Key and secret must both be correct and the app approved. Check you are not sending the publisher-level key instead of the app's.
Empty offer list Filtering came from country or platform detection. Pass country and platform explicitly, and check meta.detected to see what the API assumed.
Users click offers and nothing converts The tracking request is missing userId. Use the returned link rather than building your own.
422 INVALID_PARAMETERS Read the named fields in error.message. Common: country not two characters, limit above 100.
429 Over 60 requests per minute. Cache and back off.
Offer clicks are being fraud-rejected Add fingerprint to the tracking request. See Fingerprinting.

Related documentation