Integration API Reference Log in Get Started
#sec-iframe

iFrame Integration

The fastest way to embed our offerwall. Three steps: apply, get your keys, paste the iFrame.

Step 1 — Apply.

Create a publisher account and submit your app or website for review.

Step 2 — Get keys.

After approval, copy your Public Key (site key) and Secret Key from Setup in your dashboard.

Step 3 — Embed.

Paste the tracking link in an iFrame and configure your postback URL. Replace YOUR_KEY with your Public Key and USER_ID with your user's unique identifier.

iframe.html
<iframe
  src="https://cointomedia.com/offer/YOUR_KEY/USER_ID"
  width="100%"
  height="700"
  frameborder="0"
  allow="clipboard-write"
></iframe>
#sec-postback

Postback (Server-to-Server)

When a user completes an offer, we send a HTTP GET request to your postback URL with the parameters below. Use this to credit (and, if needed, debit) your user's balance from the server side.

Parameters

ParameterTypeDescription
subIdstringYour user's unique identifier (the one you passed as USER_ID).
transIdstringUnique transaction ID — use for deduplication.
rewardnumberAmount of virtual currency to credit.
payoutnumberOffer payout in USD.
signaturestringMD5 hash for verification (see below).
statusinteger1 = credit  ·  2 = chargeback (subtract).
offer_idstringCompleted offer ID.
offer_namestringCompleted offer name.
round_rewardnumberReward rounded to your site's decimal setting.
userIpstringUser's IP address.
countrystringISO 2-letter country code.
uuidstringUnique click ID.
event_idstringEvent ID (multi-reward offers only).
event_namestringEvent name (multi-reward offers only).

Signature verification

Always verify the signature before applying the reward or chargeback. The formula is md5(subId + transId + reward + SECRET_KEY).

postback.php
// Verify a postback before crediting
$expected = md5($_GET['subId'] . $_GET['transId'] . $_GET['reward'] . SECRET_KEY);

if (!hash_equals($expected, $_GET['signature'])) {
    http_response_code(403);
    exit('Invalid signature');
}

// Deduplicate by transId
if (transactionExists($_GET['transId'])) {
    exit('OK'); // already handled
}

if ($_GET['status'] == 1) {
    creditUser($_GET['subId'], $_GET['reward']);
} elseif ($_GET['status'] == 2) {
    debitUser($_GET['subId'], $_GET['reward']); // chargeback
}

echo 'OK';
Always respond with HTTP 200 and a short body (e.g. OK) once you've handled the event. Non-200 responses trigger retries from our Action Center.
#sec-api

Offers API

Use the Offers API when you want a custom UI and need offer data in JSON. Keep the click id from the response so your postback audit trail can connect back to the user action.

GET/api/v1/offers
Rate limit: 10 requests/hour per site credentials. Responses include an X-RateLimit-Remaining header.

Parameters

ParameterRequiredTypeDescription
site_keyrequiredstringSite public key.
site_secretrequiredstringSite secret key.
typeoptionalstringoffer or multireward.
limitoptionalintegerResults per page (1–500). Default: 100.
offsetoptionalintegerSkip N results for pagination.
countryoptionalstringISO 2-letter code (US, DE, BR).
deviceoptionalstringandroid, ios, desktop.
min_payoutoptionalfloatMinimum USD payout.
updated_sinceoptionalstringISO 8601 date for incremental sync.

Example request

offers-api.sh
# Fetch US Android offers with a minimum $1 payout
GET https://cointomedia.com/api/v1/offers?site_key=YOUR_KEY&site_secret=YOUR_SECRET
  &country=US
  &device=android
  &min_payout=1.00
  &limit=50

Example response

response.json
{
  "success": true,
  "count": 42,
  "offers": [
    {
      "offer_id": "1234",
      "name": "Complete tutorial",
      "preview": "https://.../img.jpg",
      "payout": 2.40,
      "reward": 240,
      "country": ["US"],
      "device": "android",
      "click_url": "https://.../click?uuid=..."
    }
  ]
}

Error codes

StatusMeaning
401Invalid site_key or site_secret.
403Site not approved for API access.
422Invalid parameter (see error field).
429Rate limit hit — respect X-RateLimit-Reset.
#sec-report

Report API

Fetch traffic and conversion reports for your site. Get daily, per-offer or per-country breakdowns.

GET/api/v1/reports

Query parameters

ParameterRequiredDescription
site_keyrequiredYour site key.
site_secretrequiredYour site secret.
fromoptionalStart date YYYY-MM-DD. Default: 30 days ago.
tooptionalEnd date YYYY-MM-DD. Default: today.
group_byoptionalday, offer or country. Default: day.
offer_idoptionalFilter by a specific offer ID.

Response format

report.json
{
  "success": true,
  "site": { "id": 10, "key": "YOUR_KEY" },
  "period": { "from": "2026-03-01", "to": "2026-04-03" },
  "summary": {
    "clicks": 150,
    "conversions": 12,
    "revenue": 45.50,
    "chargebacks": 1,
    "pending": 20.00,
    "paid": 25.50,
    "cvr": 8.0,
    "epc": 0.3033
  },
  "group_by": "day",
  "breakdown": [
    {
      "date": "2026-04-01",
      "clicks": 50,
      "conversions": 4,
      "revenue": 15.25,
      "chargebacks": 0,
      "cvr": 8.0,
      "epc": 0.305
    }
  ]
}

Summary fields

FieldTypeDescription
clicksintegerTotal clicks in the period.
conversionsintegerTotal conversions.
revenuenumberTotal revenue (USD).
chargebacksintegerTotal chargebacks.
pendingnumberRevenue awaiting approval.
paidnumberRevenue already paid out.
cvrnumberConversion rate (conversions ÷ clicks × 100).
epcnumberEarnings per click.

Node.js example

report.js
const axios = require("axios");

async function getReport() {
  const { data } = await axios.get("https://cointomedia.com/api/v1/reports", {
    params: {
      site_key: "YOUR_SITE_KEY",
      site_secret: "YOUR_SITE_SECRET",
      from: "2026-04-01",
      to: "2026-04-03",
      group_by: "day"   // or "offer" or "country"
    }
  });

  if (data.success) {
    console.log("Summary", data.summary);
    console.log(`Revenue: $${data.summary.revenue}`);
    data.breakdown.forEach(row =>
      console.log(`${row.date}: ${row.clicks} clicks, $${row.revenue}`)
    );
  }
}

getReport();

PHP example

report.php
$response = file_get_contents(
    "https://cointomedia.com/api/v1/reports?" . http_build_query([
        "site_key"    => "YOUR_KEY",
        "site_secret" => "YOUR_SECRET",
        "from"        => "2026-04-01",
        "to"          => "2026-04-03",
        "group_by"    => "offer",
    ])
);
$data = json_decode($response, true);

echo "Revenue: $" . $data["summary"]["revenue"];
foreach ($data["breakdown"] as $row) {
    echo $row["offer_name"] . ": " . $row["clicks"] . " clicks\n";
}
#native-banner-api

Native Banner API

Display banner ads in mobile apps (Flutter, Kotlin, Swift, React Native) or any platform that can make HTTP requests. Returns JSON with image URLs — no JavaScript required.

GET/api/banners/v1/native

Parameters

ParamRequiredDescription
site_keyYesYour site/app key from the publisher dashboard.
sub_idYesUnique user ID in your app (for tracking & anti-fraud).
sizeNoBanner size. Default 320x50. Options: 320x50, 300x250, 728x90.
limitNoMax banners to return (1–5). Default 1.

Response

native-banner.json
{
  "success": true,
  "banners": [
    {
      "id": 42,
      "image_url": "https://pro.adswedmedia.com/asset/storage/banners/example.jpg",
      "click_url": "https://pro.adswedmedia.com/api/banners/v1/click?campaign_id=42&site_key=YOUR_KEY&sub_id=123",
      "impression_url": "https://pro.adswedmedia.com/api/banners/v1/impression?campaign_id=42&site_key=YOUR_KEY&sub_id=123",
      "width": 320,
      "height": 50
    }
  ]
}

Integration steps

  1. Call the endpoint to get banner data.
  2. Display the image_url in your app.
  3. When the banner becomes visible, fire a GET request to impression_url (CPM tracking).
  4. When the user taps the banner, open click_url in the system browser.

Flutter example

banner_ad.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';

class BannerAd {
  final int id, width, height;
  final String imageUrl, clickUrl, impressionUrl;

  BannerAd.fromJson(Map<String, dynamic> json)
    : id = json['id'],
      imageUrl = json['image_url'],
      clickUrl = json['click_url'],
      impressionUrl = json['impression_url'],
      width = json['width'],
      height = json['height'];
}

Future<BannerAd?> fetchBanner(String siteKey, String userId) async {
  final uri = Uri.parse(
    'https://pro.adswedmedia.com/api/banners/v1/native'
    '?site_key=$siteKey&sub_id=$userId&size=320x50&limit=1',
  );
  final res = await http.get(uri);
  if (res.statusCode != 200) return null;
  final data = jsonDecode(res.body);
  if (data['success'] != true || (data['banners'] as List).isEmpty) return null;
  return BannerAd.fromJson(data['banners'][0]);
}

Kotlin (Android) example

BannerApi.kt
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject

suspend fun fetchBanner(siteKey: String, userId: String): JSONObject? {
  val url = "https://pro.adswedmedia.com/api/banners/v1/native" +
            "?site_key=$siteKey&sub_id=$userId&size=320x50&limit=1"
  val req = Request.Builder().url(url).get().build()
  val res = OkHttpClient().newCall(req).execute()
  if (!res.isSuccessful) return null
  val json = JSONObject(res.body!!.string())
  val banners = json.getJSONArray("banners")
  return if (banners.length() > 0) banners.getJSONObject(0) else null
}

Swift (iOS) example

BannerService.swift
import Foundation

struct BannerAd: Decodable {
  let id: Int
  let imageUrl, clickUrl, impressionUrl: String
  let width, height: Int

  enum CodingKeys: String, CodingKey {
    case id, width, height
    case imageUrl = "image_url"
    case clickUrl = "click_url"
    case impressionUrl = "impression_url"
  }
}

func fetchBanner(siteKey: String, userId: String) async throws -> BannerAd? {
  let urlStr = "https://pro.adswedmedia.com/api/banners/v1/native" +
    "?site_key=\(siteKey)&sub_id=\(userId)&size=320x50&limit=1"
  guard let url = URL(string: urlStr) else { return nil }
  let (data, _) = try await URLSession.shared.data(from: url)
  struct Wrap: Decodable { let success: Bool; let banners: [BannerAd] }
  let w = try JSONDecoder().decode(Wrap.self, from: data)
  return w.banners.first
}