OfferwallApi Integration Guide — everything you need to integrate our offerwall, APIs, and S2S postback into your website or mobile app — in just a few minutes.
Every app you register carries three credentials, all shown in the publisher cabinet under Apps & Websites → your app’s details page:
| Parameter | Type | Description |
|---|---|---|
| API_Key | varchar(32) | Identifies your website or app in every embed and API URL — the offerwall embed /offerwall/[API_KEY]/[USER_ID] and the REST feed paths. Shown as API Key in the app details. |
| Secret_Key | string | Signs your S2S postbacks (md5 signature) so your server can verify that reward calls come from us. Shown as Secret Key in the app details. |
| Bearer_Token | string | Authorizes the REST feeds (PTC API, Video API, Shortlink API and Tasks API) — send it as the Authorization: Bearer [BEARER_TOKEN] header. Shown as Bearer token in the app details (reveal, copy or regenerate it there). See Bearer authentication in the Parameters section. |
To integrate our offerwall, you first need to register your website. Once done, you will receive an API Key and a Secret Key required for all integrations.
Add App button in the sidebar to register your first website or app.Currency Name (e.g. Points, Tokens, Coins), Currency Round (decimal places, 0–8), and Exchange Rate (units earned per $1 USD).Postback URL. This endpoint will receive a call whenever a user completes an offer, allowing you to credit rewards automatically.Our offerwall is segmented, auto-translated, and fully responsive. It adapts to any screen size and serves targeted offers to your users.
Before proceeding, make sure you have your API Key, Secret Key, and Bearer Token from your app’s details page (Apps & Websites in the publisher cabinet).
Apps & Websites in the publisher cabinet — open your app details to get your full integration code and keys.Website (iFrame / JS) — embed the ready-made wall via a popup or iFrame. Fastest option, zero UI work.REST APIs — fetch raw campaign data (PTC, Video, Shortlink and Microtasks) and render it with your own UI.Native apps — open the offerwall from Android (WebView), iOS (WKWebView) or React Native (Linking / WebView).We provide ready-made integration guides for the most popular faucet scripts. Follow the steps for your specific platform below.
VieFaucet 4.3 — two controllers (postback + offerwall page), one menu link, one CSRF exclusion.VieFaucet 4.4 — same steps as 4.3 with a minor difference in the notification helper function name.CryptoFaucet / ClaimBits — create one gateway file and modify two lines; under 5 minutes.$secret variable. Without it, postback will not credit users correctly.reward is already converted to the currency you configured for your app (Currency Name / Exchange Rate). Keep the offerwall’s own currency-rate setting at in the script (and set Currency to credits where requested) so rewards are not converted twice.Whenever a user completes an offer, OfferwallApi sends an HTTP POST request to your Postback URL with all the information needed to credit your users automatically.
The postback carries the user identifier (subId), a unique transaction ID (transId), the reward in your currency and in USD, an MD5 signature plus a stronger HMAC-SHA256 signature (signature2), and the completion status (credit or chargeback). Verify a signature, check the transaction for duplicates, credit the user, and respond with exactly ok.
| Parameter | Description | Example |
|---|---|---|
| subId | Unique identifier of the user on your platform. | user123 |
| transId | Unique transaction ID — use this to prevent double-crediting. | ptc-1a2b3c4d5e6f7a8b9c0d |
| offer_name | Name of the completed offer. | OfferwallApi - Register and Earn |
OfferwallApi offers seven advertising formats to reach your audience across a network of 5,000+ active publisher websites, faucets, and GPT platforms — or grab them all at once with a discounted AIO bundle. All campaigns require admin approval before going live.
| Type | Billing Model | Best For | Min. Budget |
|---|---|---|---|
| PTC / PTP | Per click (CPC) | Traffic, website visits | Per pack (see rates) |
| Video Ads | Per view (CPV) | YouTube video promotion | Per pack (see rates) |
| Shortlink | Reward × visits × tier multiplier | Link monetization, traffic | $0.10 (100 visits × $0.001) |
Every endpoint accepts form-encoded POST bodies and returns JSON.
Your faucet api_key authenticates every request. Keep it server-side.
Send ip_address with /send to power cross-faucet abuse detection.
https://faucetpay.io/api/v1All endpoints are POST, form-encoded and return JSON. The envelope is identical across all endpoints so your client code can share parsing logic.
Content-Type: application/x-www-form-urlencoded
api_key=YOUR_API_KEY
amount=100
[email protected]
currency=BTC
ip_address=203.0.113.4{
"status": 200,
"message": "Payout completed successfully!",
// …endpoint-specific fields
}| Parameter | Type |
|---|
Your site must credit a conversion only when the postback sub_id exactly matches the user ID your site originally sent — with no changes of any kind. We have detected sites "fixing" mismatched IDs by cutting off part of the sub_id to force a match. This is not allowed: trimming, truncating, padding, or otherwise altering the sub_id results in conversions being credited to the wrong or manipulated users.
Our system returns the sub_id exactly as received (up to 64 characters) and sends it across a range of user ID formats. Because of this, we cannot whitelist or make exceptions for any format — the only acceptable behavior is a full, exact-match comparison. Any site found accepting postbacks on a non-exact match will be rejected and removed from the network. This applies to the site as a whole, not just the affected conversions.
| Field | Range | Description |
|---|---|---|
| Currency_Name | string | Shown to your users as the reward unit (e.g. Points, Tokens, Coins). |
| Currency_Round | 0–8 | Decimal places used when displaying rewards. |
| Exchange_Rate | number | Units of your currency per $1 USD payout. |
| Postback_URL | https URL | Must match your app domain. Receives the S2S postback on every completion. |
Timed tasks (PTC / Video / Shortlink) follow a captcha-first flow: when the platform has the start captcha enabled (an admin toggle, on by default), the user must solve a captcha before the task can be started. Only after solving it does the ad open and the countdown run — and rewards are only credited after the timer completes. When your users see the wall through the embed URL below, this happens automatically inside the wall; nothing extra is required on your side.
Embed the offerwall via JavaScript popup or iFrame — replace [API_KEY] and [USER_ID] with your actual values.
window.open("/offerwall/[API_KEY]/[USER_ID]")Keep allow="notifications" in the tag — it lets the wall deliver push campaigns as real browser notifications inside the iframe (without it, push ads are still delivered as in-wall cards).
<iframe
scrolling="yes"
frameborder="0"
allow="notifications"
style="width:100%;height:800px;border:0;"
src="/offerwall/[API_KEY]/[USER_ID]">
</iframe>[API_KEY] with your website API key and [USER_ID] with the unique identifier of the user currently viewing the wall.PTC and Shortlink ads open in a new tab while the timer runs on our wall. When the time is up, a beep calls the user back and the reward is claimed automatically on return — no action needed on your side. VIDEO rewards count only actual playback time (the timer pauses when the video is paused).
Crediting happens through your S2S postback: a delivered postback with status ok means the user is already credited on your site. A pending delivery is retried automatically — see the S2S Postback section.
Fetch PTC campaign data directly and render it with your own UI. Useful when you want a fully custom look and feel.
curl -X GET "/ptc-api/[API_KEY]/[USER_ID]/[USER_IP]" \
-H "Authorization: Bearer [BEARER_TOKEN]"{
"status": "200",
"message": "success",
"data": [
{
"id": "8098",
"image": "",
"title": "Free spin, win BTC! Up to 5 BTC daily!",
"description": "760% deposit bonus! Earn While Playing!",
"duration": "30",
"reward": "600.00",
"currency_name": "Cash",
"url": "https://offerwallapi.com/ptc-view/[API_KEY]/[USER_ID]?cid=6h2m9x4p1q",
"boosted_campaign": false,
"ad_type": "Iframe"
}
]
}The url field points to our hosted timed-view page. When your user opens it, the ad opens in a new tab, a server-bound timer runs on our page, a beep signals when the view is complete, and the reward is claimed automatically the moment the user returns — the advertiser is charged, your publisher account is credited and your S2S postback fires. The URL is final: the [USER_ID] from your feed request is already embedded, so no substitution is needed.
360px is recommended.ok) means the user is credited on your site./api/offerwall/click?k=[API_KEY]&u=[USER_ID]&cid=[CAMPAIGN_ID] — it records the click only, without charging the advertiser or paying a reward.<?php
function requestWithFileGetContents($url, $token) {
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$options = [
'http' => [
'header' => "Authorization: Bearer $token\r\nUser-UA: $userAgent\r\n",
]
];
return file_get_contents($url, false, stream_context_create($options));
}
function requestWithCurl($url, $token) {
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token",
"User-UA: $userAgent"
],
]);
$response = curl_exec($ch);
if (curl_errno($ch)) $response = false;
curl_close($ch);
return $response;
}
$url = '/ptc-api/[API_KEY]/[USER_ID]/[USER_IP]';
$token = '[BEARER_TOKEN]';
$response = requestWithFileGetContents($url, $token) ?: requestWithCurl($url, $token);
if ($response) {
$data = json_decode($response, true);
if (isset($data['status']) && $data['status'] == 200) {
foreach ($data['data'] as $ptc) {
echo $ptc['title'] . " — " . $ptc['reward'] . "\n";
}
} else {
echo $data['message'];
}
} else {
echo "Request Failed";
}The requestWithFileGetContents() / requestWithCurl() helpers defined here are reused by the Video, Shortlink and Tasks API examples below.
Fetch video campaigns directly and render them with your own UI — the same Bearer-authenticated feed as the PTC API, for the Video category.
curl -X GET "/video-api/[API_KEY]/[USER_ID]/[USER_IP]" \
-H "Authorization: Bearer [BEARER_TOKEN]"{
"status": "200",
"message": "success",
"data": [
{
"id": "6h2m9x4p1q",
"image": "",
"title": "Watch: Top 5 crypto trends of the year",
"description": "Watch the full video to earn the reward.",
"duration": "60",
"reward": "450.00",
"currency_name": "Cash",
"url": "https://offerwallapi.com/ptc-view/[API_KEY]/[USER_ID]?cid=6h2m9x4p1q&type=VIDEO",
"boosted_campaign": false,
"ad_type": "Iframe"
}
]
}The url field points to our hosted video page with type=VIDEO. When your user opens it, the page shows the captcha first (when enabled), then the video starts — a server-bound timer runs on our page and the reward is claimed automatically when the watch time is reached — the advertiser is charged, your publisher account is credited and your S2S postback fires. The URL is final: the [USER_ID] from your feed request is already embedded, so no substitution is needed.
ad_type: "Iframe" — a YouTube video: the player embeds directly on the hosted page and only actual playback time counts (the timer pauses when the video is paused).ad_type: "Window" — any other video source: the video opens in a new tab and the timer runs on our page, exactly like a PTC ad — the user must keep the video tab open for the whole duration.url as-is (new tab or iFrame, min 360px wide) — rewards complete through your postback like every other category.<?php
// Reuses requestWithFileGetContents() / requestWithCurl() from the PTC API example
$url = '/video-api/[API_KEY]/[USER_ID]/[USER_IP]';
$token = '[BEARER_TOKEN]';
$response = requestWithFileGetContents($url, $token) ?: requestWithCurl($url, $token);
if ($response) {
$data = json_decode($response, true);
if (isset($data['status']) && $data['status'] == 200) {
foreach ($data['data'] as $video) {
echo $video['title'] . " — " . $video['reward'] . " — " . $video['ad_type'] . "\n";
}
} else {
echo $data['message'];
}
} else {
echo "Request Failed";}Fetch shortlink campaigns directly from our API to display them in your own UI.
curl -X GET "/sl-api/[API_KEY]/[USER_ID]/[USER_IP]" \
-H "Authorization: Bearer [BEARER_TOKEN]"{
"status": "200",
"message": "success",
"data": [
{
"id": "1",
"title": "earnow.online",
"reward": "600.00",
"currency_name": "Cash",
"completion_rate": "100.00",
"available": "2",
"limit": "2",
"url": "https://offerwallapi.com/ptc-view/[API_KEY]/[USER_ID]?cid=6h2m9x4p1q&type=SHORTLINK",
"boosted_campaign": false
}
]
}The url field points to our hosted lead page with type=SHORTLINK. When your user opens it, the page shows the captcha first (when enabled), then a Start View Ad button — the shortlink opens in a new tab, a server-bound timer runs on our page, a beep signals when the view is complete, and the reward is claimed automatically the moment the user returns — the advertiser is charged, your publisher account is credited and your S2S postback fires. The URL is final: the [USER_ID] from your feed request is already embedded, so no substitution is needed.
The feed path always includes the [USER_ID] segment, so every item hands out the hosted lead page. A legacy raw click link (/api/offerwall/click?…) is kept for callers that fetch the feed without an end-user id — it 302-redirects to the advertiser page and records the click only, without timing or crediting the user.
<?php
// Reuses requestWithFileGetContents() / requestWithCurl() from the PTC API example
$url = '/sl-api/[API_KEY]/[USER_ID]/[USER_IP]';
$token = '[BEARER_TOKEN]';
$response = requestWithFileGetContents($url, $token) ?: requestWithCurl($url, $token);
if ($response) {
$data = json_decode($response, true);
if (isset($data['status']) && $data['status'] == 200) {
foreach ($data['data'] as $shortlink) {
echo $shortlink['title'] . " — " . $shortlink['reward'] . "\n";
}
} else {
echo $data['message'];
}
} else {
echo "Request Failed";
}Microtasks over the Bearer-authenticated REST API: fetch the tasks feed for your end user, render the task page inside your own UI, and submit the proof server-to-server. The review pipeline is identical to the embedded wall — the advertiser reviews the proof, the escrow is charged on approval, your publisher account is credited your share (70% by default) and your S2S postback fires with offer_type=microtask.
Unlike the timed categories there is no hosted page and no countdown: the url field is always null — your site renders the task page itself (the full instructions are part of the feed) and submits the proof through the API once the user has done the job.
curl -X GET "/task-api/[API_KEY]/[USER_ID]/[USER_IP]" \
-H "Authorization: Bearer [BEARER_TOKEN]"{
"status": "200",
"message": "success",
"data": [
{
"id": "cmx8t2p4q0001ab2c3d4e5f6",
"title": "Leave a comment on our blog post",
"description": "Read the article and leave a meaningful comment (min 2 sentences)…",
"instructions": "1. Open the blog post.\n2. Read it.\n3. Leave a meaningful comment of at least two sentences.\n4. Copy the link to your comment and submit it as the proof.",
"requirements": "Link to your comment",
"proofType": "URL",
"proofHint": "Link to your comment",
"targetUrl": "https://advertiser.example/blog/post",
"reward": "420.00",
"currency_name": "Cash",
"reward_usd": 0.42,
"status": "available",
"url": null
}
],
"approved": ["cmx8t1q0r0009ab2c3d4e5f7"]
}| Field | Type | Description |
|---|---|---|
| id | string | Task id — pass it as taskId when submitting the proof. |
| title | string | Task title shown on the card. |
| description | string | Instructions teaser (up to 160 characters) — render it as the card text. |
| instructions | string | The FULL task instructions — render them on your own task page (the wall shows them inside the task modal). |
| requirements | string | The proof requirement — same value as proofHint. |
| proofType | string | URL, TEXT or IMAGE_URL — the shape of the proof the user must submit (see proof validation in the Microtasks section). |
| proofHint | string | What the advertiser requires as proof (e.g. “link to your comment”). |
| targetUrl | string | The task page — open it in a new tab when the user accepts the task. |
The same gates as the wall apply server-side: only ACTIVE tasks that match the user’s country and device (resolved server-side from the [USER_IP] path segment, or override it with ?cc=XX), still have escrow left for one full reward and are not past their daily limit are listed.
When the user has completed the job, submit the proof from your server. The request uses the same Bearer authentication and the same path as the feed — the body carries the task id and the proof:
curl -X POST "/task-api/[API_KEY]/[USER_ID]/[USER_IP]" \
-H "Authorization: Bearer [BEARER_TOKEN]" \
-H "Content-Type: application/json" \
-d '{"taskId":"cmx8t2p4q0001ab2c3d4e5f6","proof":"https://advertiser.example/blog/post#comment-42"}'| Parameter | Required | Description |
|---|---|---|
| taskId | Yes | The id field of the task feed item. |
| proof | Yes | The proof of completion — shape and length limits depend on the task's proofType (see proof validation in the Microtasks section). |
| fp | No | Optional device / browser fingerprint from your UI (max 64 characters) — used by the anti-fraud engine. |
// success — the submission is now PENDING review
{ "status": "200", "message": "Submitted for review", "data": [] }
// this user already has a pending or approved submission for the task
{ "status": "409", "message": "You have already submitted this task. It is pending review or already approved.", "data": [] }
// the [USER_IP] path segment is missing or not a plausible IPv4 address
{ "status": "400", "message": "Invalid [USER_IP] — pass the end user's IPv4 address in the path", "data": [] }The proof is validated against the task’s proofType exactly like the wall endpoint (URL / IMAGE_URL: valid http(s) URL, max 2048 characters; TEXT: 1–2000 characters), and every wall gate applies: one PENDING or APPROVED submission per task per user, daily submission limits, the per-user cap on simultaneous pending submissions and the advertiser escrow.
<?php
// Feed: reuse the request helpers from the PTC API example
$feed = requestWithFileGetContents('/task-api/[API_KEY]/[USER_ID]/[USER_IP]', '[BEARER_TOKEN]');
$tasks = $feed ? (json_decode($feed, true)['data'] ?? []) : [];
foreach ($tasks as $task) {
if ($task['status'] === 'available') {
// render the card: $task['title'], $task['description'], $task['reward'] …
} else {
// pending review — show the "awaiting review" state (submittedAt available)
}
}
// Submission: POST the proof for the task the user completed
$payload = json_encode([
'taskId' => $taskId, // the "id" field from the feed item
'proof' => $proof, // shaped by the task's proofType
]);
$ch = curl_init('/task-api/[API_KEY]/[USER_ID]/[USER_IP]');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer [BEARER_TOKEN]',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $payload,
]);
$response = curl_exec($ch);
if (curl_errno($ch)) $response = false;
curl_close($ch);
if ($response) {
$data = json_decode($response, true);
if (isset($data['status']) && $data['status'] == 200) {
echo 'Submitted for review';
} else {
echo $data['message'] ?? 'Submission failed';
}
} else {
echo 'Request Failed';
}Submissions go through the exact same review pipeline as the embedded wall — see the Microtasks section for the lifecycle (advertiser / admin review, auto-approval window, referral share) and the proof validation rules. The task-api answers with the legacy body status codes instead of HTTP codes:
| status | message | Cause |
|---|---|---|
| 200 | Submitted for review | Proof accepted — the submission is PENDING. |
| 400 | Missing parameters… / Invalid [USER_IP]… / Proof must be… | taskId or proof missing, the [USER_IP] path segment is not a plausible IPv4, or the proof does not match the proofType rules. |
| 403 | Invalid API key / Invalid bearer token | Unknown or unapproved API key, or wrong bearer token. |
| 403 | Not available in your region / VPN, proxy or datacenter traffic… | Country or device targeting excluded the end user, or the end user’s IP is a VPN while VPN blocking is enabled. |
| 404 | Task unavailable / Task budget exhausted | Unknown task id, task not ACTIVE, or the escrow cannot cover one more reward. |
| 409 | You have already submitted this task… | Duplicate: this user already has a PENDING or APPROVED submission for the task. |
offer_type=microtask and transId=task:{submissionId} — the reward is your credited share in your currency. See the S2S Postback section for the full callback contract.Open the offerwall in Chrome or embed it in a WebView inside your Android app.
https://offerwallapi.com/offerwall/[API_KEY]/[USER_ID]WebView myWebView = new WebView(activityContext);
setContentView(myWebView);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.loadUrl("https://offerwallapi.com/offerwall/[API_KEY]/[USER_ID]");Open the offerwall in Safari or use WKWebView inside your iOS app.
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: "https://offerwallapi.com/offerwall/[API_KEY]/[USER_ID]")
webView.load(URLRequest(url: myURL!))
}
}Use the Linking API to open in the system browser, or embed with react-native-webview.
import { Linking } from 'react-native';
Linking.openURL('https://offerwallapi.com/offerwall/[API_KEY]/[USER_ID]');import { WebView } from 'react-native-webview';
export default function OfferwallScreen() {
return (
<WebView
source={{ uri: 'https://offerwallapi.com/offerwall/[API_KEY]/[USER_ID]' }}
style={{ flex: 1 }}
/>
);
}Placeholders used across the offerwall URL and the REST API endpoints.
| Parameter | Type | Description |
|---|---|---|
| [API_KEY] | varchar(32) | Unique API code provided when you registered your website. |
| [USER_ID] | varchar(64) | Unique identifier of the user viewing the wall on your site. Values longer than 64 characters are truncated server-side. |
| [USER_IP] | string | IP address of the user (for API endpoints). The path segment is kept for compatibility — geo targeting is resolved server-side from the actual request IP. |
| [BEARER_TOKEN] | string | Authorization token for the PTC, Video, Shortlink and Microtask REST feeds. Find it in the app details page (Apps & Websites → app details → Bearer token). |
The PTC, Video, Shortlink and Microtask REST feeds are authorized with a bearer token. Send it in the Authorization header of every request:
Authorization: Bearer [BEARER_TOKEN]Apps & Websites → open your app details → Bearer token (reveal, copy, or regenerate it there).401; an unknown key, an unapproved app or a wrong token answers 403 — see the error table below.429 — back off and retry.ptc-api, video-api, sl-api and task-api — so your site can render every task type in its own UI.The PTC, Video and Shortlink feed paths always include the [USER_ID] segment, so every url field is the hosted lead page ( /ptc-view/…) with your [USER_ID] already embedded — render it as-is (see the PTC API, Video API and Shortlink API sections above). The Tasks feed has no click URLs — your UI renders the task page and submits proofs through the API.
A legacy fallback exists for callers that fetch the feeds without an end-user id: those items point to our click endpoint and contain the literal u=SUBID placeholder. Replace SUBID with the end-user’s identifier before rendering the link, so conversions are attributed to the right user in your S2S postback. A bare click link records the click only, without timing or crediting the user.
https://offerwallapi.com/api/offerwall/click?k=[API_KEY]&u=user123&cid=[CAMPAIGN_ID]The PTC, Video, Shortlink and Microtask APIs always answer with HTTP 200 — errors are signalled by the status field in the body (the PHP examples above already branch on it):
| status | message | Cause |
|---|---|---|
| 200 | success | Request OK — data contains the campaign list. |
| 401 | Missing bearer token | No Authorization: Bearer header sent. |
| 403 | Invalid API key / Invalid bearer token | Unknown or unapproved API key, or wrong bearer token. |
| 429 | Too many requests. Try again later | Rate limit exceeded (60/min per IP, 120/min per API key). |
On errors data is always an empty array. The offerwall feed endpoints used by the custom-UI flow below use real HTTP status codes instead — see their error documentation.
A small server-side REST flow for building a fully custom offerwall UI: fetch the feed, render it with your own markup, and run timed ads (PTC / Video / Shortlink) through the view-session endpoint. Microtasks are served by the same feed and submitted through their own endpoint — see the Microtasks section below. No SDK file — just a few endpoints that answer JSON.
curl -X GET "https://offerwallapi.com/api/offerwall?k=[API_KEY]&u=[USER_ID]&category=ptc"| Parameter | Required | Description |
|---|---|---|
| k | Yes | Your API key. The app must be APPROVED — otherwise 403. |
| u | Yes | The end-user's identifier (Sub ID). Max 64 characters. |
| category | No | ptc / video / shortlinks / push / tasks. Omit for the default combined feed (PTC + Video + Shortlinks + microtasks). Unknown category values return an empty items list. |
{
"app": { "name": "My Site", "currency_name": "Points", "round": 2, "rate": 1000 },
"user": "user123",
"geo": { "country": "US", "tier": 1 },
"balance": 0.25,
"claimCaptcha": false,
"startCaptcha": true,
"completedToday": ["cmd4x7p2a0008l9zq1t3v6h0k"],
"items": [
{
"type": "ptc",
"id": "cmd4x7p2a0008l9zq1t3v6h0k",
"title": "Visit this page for 30 seconds",
"description": "Advertiser description shown to the user.",
"requirements": "View the page for 30 seconds",
"reward": 0.27,
"reward_usd": 0.00027,
"reward_name": "Points",
"duration": 30,
"adMode": "IFRAME",
"source": "PTC",
"url": null
}
]
}reward is already converted to your currency (reward_usd holds the USD value; rate / round in app show the conversion used). balance is the user’s credited offerwall earnings in USD. startCaptcha tells you whether the start request (step 3) must include a solved captcha. completedToday lists what this user (same IP or Sub ID) already completed today — campaign ids, plus task:{taskId} keys for microtasks this user submitted to (pending review or approved). Completed tasks are excluded from items (like every offerwall provider, a completed task disappears from the available list until the daily reset), so the array is your reference for “N of M tasks done today” UIs. Item shapes by category:
ptc — carries duration and adMode (IFRAME or WINDOW); url is null — start a view session instead (step 3).video — same shape as PTC with adMode always IFRAME; the timer counts only actual playback time.shortlink — a tracked click url that opens the advertiser’s shortlink (step 2); the reward itself is claimed through the view session (step 3).push — push campaigns are delivered as browser notifications while the wall is open (only for users who granted the notification permission — the wall header bell is the opt-in). Thecategory=push feed powers the wall client’s auto-delivery loop; push items never render as wall cards. Users earn nothing for push — the publisher is credited per delivered notification and per click from the admin push tariffs.curl -X POST "https://offerwallapi.com/api/offerwall/click" -H "Content-Type: application/json" -d '{"k":"[API_KEY]","u":"user123","cid":"[CAMPAIGN_ID]"}'{
"ok": true,
"url": "https://advertiser.example/landing?click_id=a1b2...",
"clickId": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
"durationSec": 30,
"adMode": "WINDOW",
"type": "SHORTLINK"
}The click is recorded server-side and url is the advertiser’s destination with all macros ({click_id}, {sub_id}, {campaign_id}, {ip_address}, {country_code}) already applied — send the user to it in a new tab. type is PTC or SHORTLINK; durationSec and adMode mirror the campaign (null when not applicable). A GET to the same endpoint with the same parameters (?k=&u=&cid=) 302-redirects straight to url. The click records the visit only — rewards are credited through the view session below.
Start the timed view, show the ad, then claim the reward after the duration elapsed. When the feed returned startCaptcha: true, the start request must include a solved captcha — the user solves the challenge BEFORE the ad opens (captcha-first flow):
// GET /api/captcha → { "id": "challenge-id", "svg": "<svg …>" } (render the svg, collect the answer)
// POST /api/offerwall/ptc — body: { "action": "start", "k": "[API_KEY]", "u": "user123", "cid": "[CAMPAIGN_ID]",
// "type": "PTC", "captchaId": "challenge-id", "captchaAnswer": "42" }
{ "ok": true, "token": "ptc-session-token", "duration": 30, "targetUrl": "https://advertiser.example", "adMode": "IFRAME" }
// POST /api/offerwall/ptc — body: { "action": "claim", "k": "...", "u": "user123", "cid": "...", "type": "PTC",
// "token": "ptc-session-token", "hiddenMs": 29500 }
{ "ok": true, "credited": true, "reward": 0.00027,
"postback": { "delivered": true, "response": "ok", "httpStatus": 200 } }type is PTC, VIDEO or SHORTLINK (matching the feed item). Open targetUrl for duration seconds according to adMode, then claim: PTC and SHORTLINK claims report hiddenMs — how long your page was hidden while the user was on the ad tab — and VIDEO claims report watchMs, the accumulated actual playback time. The captcha challenge comes from GET /api/captcha (returns { id, svg } — pass id as captchaId and the user’s answer as captchaAnswer; the same fields are accepted on claim when the platform enables the claim-time captcha, which is off by default). reward is USD; it can be 0 with credited: false when the visit does not qualify (same IP or Sub ID already completed this ad today, budget exhausted). postback reports the delivery of your S2S postback for this completion (delivered, your endpoint’s response,httpStatus) — null means nothing was credited, so no postback was sent.
Unlike the REST feeds above, these endpoints use real HTTP status codes and always return a JSON body of { "error": "..." } on failure:
| HTTP | error | Cause |
|---|---|---|
| 400 | Missing parameters | k, u or cid is missing; a claim additionally requires the session token. |
| 403 | Invalid API key | Unknown or unapproved app key. The /api/offerwall feed returns "Invalid or unapproved API key" for the same cause. |
| 403 | Not available in your region | Country / tier / device targeting excluded the user (the click endpoint words it "Offer not available in your region"). |
| 403 | Captcha verification failed | Wrong or expired captcha answer on start (or claim, when the claim captcha is enabled). |
| 403 | Invalid or expired session | The claim token does not match this campaign / user / IP, or was already consumed. |
| 403 | Access denied | The request IP is banned. |
| 403 | VPN, proxy or datacenter traffic is not allowed… |
[API_KEY] with your API key and [USER_ID] / user123 with your Sub ID (your website’s user identifier) — the same values used throughout this Integration section.Microtasks are advertiser-defined jobs with a proof of completion: the user opens the task page, does what the instructions ask — leave a comment, create an account, post a screenshot — and submits a proof for review. The reward is set by the advertiser ($0.005–$50 per approved completion, escrowed up-front) and your publisher account is credited your share of it — 70% by default (the platform’s microtask publisher share).
Unlike timed ads there is no countdown: the submission goes to review and the reward is paid when the advertiser (or a platform admin) approves it. The embedded offerwall gets a Tasks tab automatically — nothing to enable on your side. For a fully custom UI you have two options: the server-side feed below (API key based), or the Tasks API (Bearer-authenticated task-api — see the Tasks API section) which returns the full instructions and submits proofs server-to-server.
curl -X GET "https://offerwallapi.com/api/offerwall?k=[API_KEY]&u=[USER_ID]&category=tasks"Tasks are also part of the default combined feed (omit category) — every item with type: "task" is a microtask. The same gates as the wall apply server-side: only ACTIVE tasks that match the user’s country and device, still have escrow left for one full reward and are not past their daily limit are listed. A task the user already has an APPROVED submission for never reappears; a PENDING submission keeps the task in the feed so your UI can show the “pending review” state — the completedToday array carries task:{taskId} keys for both cases.
| Field | Type | Description |
|---|---|---|
| type | string | Always task — the microtask marker in the feed. |
| id | string | Task id — pass it as taskId when submitting the proof. |
| title | string | Task title shown on the card. |
| description | string | Instructions teaser (up to 160 characters) — render it as the card text. |
| requirements | string | The proof requirement — same value as proofHint. |
| reward | number | User-facing reward in your currency: your share of the advertiser reward, converted at your exchange rate. |
| reward_usd | number | Your share of the advertiser reward, in USD. |
| reward_name | string | Your currency name. |
| proofType | string |
When the user has completed the job, submit the proof to the task endpoint. It answers real HTTP status codes with a JSON body — { ok: true } on success, { error: "…" } on failure.
curl -X POST "https://offerwallapi.com/api/offerwall/task" \
-H "Content-Type: application/json" \
-d '{"k":"[API_KEY]","u":"user123","taskId":"[TASK_ID]","proof":"https://example.com/my-comment"}'| Parameter | Required | Description |
|---|---|---|
| k | Yes | Your API key. The app must be APPROVED — otherwise 403. |
| u | Yes | The end-user's identifier (Sub ID). Max 64 characters. |
| taskId | Yes | The id field of the task feed item. |
| proof | Yes | The proof of completion — shape and length limits depend on the task's proofType (see below). |
| fp | No | Optional device / browser fingerprint from your UI (max 64 characters) — used by the anti-fraud engine. |
// 200 — accepted, now pending review
{ "ok": true, "message": "Submitted for review" }
// 409 — this user already has a pending or approved submission for the task
{ "error": "You have already submitted this task. It is pending review or already approved.", "duplicate": true }
// 429 — submission rate limit (see below)
{ "error": "Too many requests. Try again in 42s" }<?php
$payload = json_encode([
'k' => '[API_KEY]',
'u' => $userId, // your user's id (Sub ID)
'taskId' => $taskId, // the "id" field from the feed item
'proof' => $proof, // shaped by the task's proofType
]);
$ch = curl_init('https://offerwallapi.com/api/offerwall/task');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => $payload,
]);
$response = curl_exec($ch);
if (curl_errno($ch)) $response = false;
curl_close($ch);
if ($response) {
$data = json_decode($response, true);
if (isset($data['ok']) && $data['ok']) {
echo 'Submitted for review';
} else {
echo $data['error'] ?? 'Submission failed';
}
} else {
echo 'Request Failed';
}The proof is validated against the task’s proofType before anything is written — build your form accordingly:
| proofType | Expected proof | Limits |
|---|---|---|
| URL | A link to the result of the task (e.g. the user's comment or post). | Valid http(s) URL, max 2048 characters. |
| IMAGE_URL | A link to a screenshot of the completion. | Valid http(s) URL, max 2048 characters. |
| TEXT | A free-text answer typed by the user. | 1–2000 characters. |
targetUrl in a new tab and completes the instructions.proofType, hint from proofHint) and submits it — the submission is now PENDING.429 with a retry hint when exceeded.429 "too many submissions awaiting review" above it.409.404 Task budget exhausted and the task leaves the feed.| HTTP | error | Cause |
|---|---|---|
| 400 | Missing parameters | k, u, taskId or proof is missing. |
| 400 | Proof must be a valid http(s) URL / Proof URL is too long… / Proof text must be 1-2000 characters | The proof does not match the task's proofType rules (see proof validation above). |
| 403 | Access denied | The request IP is banned. |
| 403 | Invalid API key | Unknown or unapproved app key. |
| 403 | VPN, proxy or datacenter traffic is not allowed for this task. | The submission came from a VPN/proxy/datacenter IP while VPN blocking is enabled. |
| 403 | Not available in your region | Country or device targeting excluded the user. |
| 404 | Task unavailable / Task budget exhausted | Unknown task id, task not ACTIVE, or the escrow cannot cover one more reward. |
offer_type=microtask and transId=task:{submissionId} — the reward is your credited share in your currency. See the S2S Postback section for the full callback contract (signature verification, the ok response, the retry schedule).1Four steps to integrate the OfferwallApi offerwall into VieFaucet 4.3.
Open application/controllers/wh.php and add this before the last closing bracket }:
public function offerwallapi()
{
$secret = ""; // YOUR SECRET KEY
$hold = 3; // Hold days (0 = instant)
$minHold = 0.5; // Rewards below this are not held
$userId = isset($_REQUEST['subId']) ? $this->db->escape_str($_REQUEST['subId']) : null;
$transactionId = isset($_REQUEST['transId']) ? $this->db->escape_str($_REQUEST['transId']) : null;
$reward = isset($_REQUEST['reward']) ? $this->db->escape_str($_REQUEST['reward']) : null;
$action = isset($_REQUEST['status']) ? $this->db->escape_str($_REQUEST['status']) : null;
$userIp = isset($_REQUEST['userIp']) ? $this->db->escape_str($_REQUEST['userIp']) : "0.0.0.0";
$signature = isset($_REQUEST['signature']) ? $this->db->escape_str($_REQUEST['signature']) : null;
if (md5($userId . $transactionId . $reward . $secret) != $signature) {
echo "ERROR: Signature doesn't match";
return;
}
$reward = $reward * $this->data['settings']['currency_rate'];
$trans = $this->m_offerwall->getTransaction($transactionId, 'offerwallapi');
if ($action == 2) {
$this->m_offerwall->reduceUserBalance($userId, abs($reward));
$this->m_offerwall->insertTransaction($userId, 'OfferwallApi', $userIp, $reward, $transactionId, 1, time());
echo "ok";
} else {
if (!$trans) {
$hold = ($reward > $minHold) ? 3 : 0;
if ($hold == 0) {
$offerId = $this->m_offerwall->insertTransaction($userId, 'OfferwallApi', $userIp, $reward, $transactionId, 2, time());
$this->m_offerwall->updateUserBalance($userId, $reward);
$this->m_core->addNotification($userId, currency($reward, $this->data['settings']['currency_rate']) . " from OfferwallApi Offer #$offerId was credited.", 1);
$user = $this->m_core->get_user_from_id($userId);
$this->m_core->addExp($user['id'], $this->data['settings']['offerwall_exp_reward']);
if (($user['exp'] + $this->data['settings']['offerwall_exp_reward']) >= ($user['level'] + 1) * 100) {
$this->m_core->levelUp($user['id']);
}
} else {
$availableAt = time() + $hold * 86400;
$offerId = $this->m_offerwall->insertTransaction($userId, 'OfferwallApi', $userIp, $reward, $transactionId, 0, $availableAt);
$this->m_core->addNotification($userId, "Your OfferwallApi Offer #$offerId is pending approval.", 0);
}
echo "ok";
} else {
echo "DUP";
}
}
}Open application/controllers/offerwall.php and add before the last }:
public function offerwallapi()
{
$api_key = ""; // YOUR API KEY
$this->data['page'] = 'OfferwallApi Offerwall';
$this->data['iframe'] = '<iframe style="width:100%;height:800px;border:0;" scrolling="yes" frameborder="0" allow="notifications"
src="https://offerwallapi.com/offerwall/' . $api_key . '/' . $this->data['user']['id'] . '"></iframe>';
$this->data['wait'] = 3; // Hold days
$this->render('offerwall', $this->data);
}Add a menu link in application/views/user_template/template.php:
<li><a href="<?= site_url('offerwall/offerwallapi') ?>" key="t-offerwallapi">OfferwallApi</a></li>Open Application/Config/config.php, find $config['csrf_exclude_uris'] and add:
'wh/offerwallapi',https://yourdomain.com/wh/offerwallapiIntegration for VieFaucet 4.4 follows the same steps as 4.3 with a minor difference in the notification helper function name.
Open application/controllers/wh.php and add this function:
public function offerwallapi()
{
$secret = ""; // YOUR SECRET KEY
$hold = 3;
$minHold = 0.5;
$userId = isset($_REQUEST['subId']) ? $this->db->escape_str($_REQUEST['subId']) : null;
$transactionId = isset($_REQUEST['transId']) ? $this->db->escape_str($_REQUEST['transId']) : null;
$reward = isset($_REQUEST['reward']) ? $this->db->escape_str($_REQUEST['reward']) : null;
$action = isset($_REQUEST['status']) ? $this->db->escape_str($_REQUEST['status']) : null;
$userIp = isset($_REQUEST['userIp']) ? $this->db->escape_str($_REQUEST['userIp']) : "0.0.0.0";
$signature = isset($_REQUEST['signature']) ? $this->db->escape_str($_REQUEST['signature']) : null;
if (md5($userId . $transactionId . $reward . $secret) != $signature) {
echo "ERROR: Signature doesn't match";
return;
}
$reward = $reward * $this->data['settings']['currency_rate'];
$trans = $this->m_offerwall->getTransaction($transactionId, 'offerwallapi');
if ($action == 2) {
$this->m_offerwall->reduceUserBalance($userId, abs($reward));
$this->m_offerwall->insertTransaction($userId, 'OfferwallApi', $userIp, $reward, $transactionId, 1, time());
echo "ok";
} else {
if (!$trans) {
$hold = ($reward > $minHold) ? 3 : 0;
if ($hold == 0) {
$offerId = $this->m_offerwall->insertTransaction($userId, 'OfferwallApi', $userIp, $reward, $transactionId, 2, time());
$this->m_offerwall->updateUserBalance($userId, $reward);
// Note: 4.4 uses currencyDisplay() instead of currency()
$this->m_core->addNotification($userId, currencyDisplay($reward, $this->data['settings']) . " from OfferwallApi Offer #$offerId was credited.", 1);
$user = $this->m_core->getUserFromId($userId);
$this->m_core->addExp($user['id'], $this->data['settings']['offerwall_exp_reward']);
if (($user['exp'] + $this->data['settings']['offerwall_exp_reward']) >= ($user['level'] + 1) * 100) {
$this->m_core->levelUp($user['id']);
}
} else {
$availableAt = time() + $hold * 86400;
$offerId = $this->m_offerwall->insertTransaction($userId, 'OfferwallApi', $userIp, $reward, $transactionId, 0, $availableAt);
$this->m_core->addNotification($userId, "Your OfferwallApi Offer #$offerId is pending approval.", 0);
}
echo "ok";
} else {
echo "DUP";
}
}
}Identical to VieFaucet 4.3. See the section above.
https://yourdomain.com/wh/offerwallapiCreate one file and modify two lines — under 5 minutes.
Create the file system/gateways/offerwallapi.php with the following content:
<?php
define('BASEPATH', true);
require('../init.php');
$secret = "YOUR_SECRET_KEY";
$userId = isset($_REQUEST['subId']) ? $_REQUEST['subId'] : null;
$transId = isset($_REQUEST['transId']) ? $_REQUEST['transId'] : null;
$reward = isset($_REQUEST['reward']) ? $_REQUEST['reward'] : null;
$status = isset($_REQUEST['status']) ? $_REQUEST['status'] : null;
$signature = isset($_REQUEST['signature']) ? $_REQUEST['signature'] : null;
$userIp = isset($_REQUEST['userIp']) ? $_REQUEST['userIp'] : "0.0.0.0";
if (md5($userId . $transId . $reward . $secret) != $signature) {
echo "ERROR: Signature doesn't match";
exit;
}
if ($status == 2) {
// Chargeback — subtract reward
// Your chargeback logic here
} else {
// Credit reward to user
// Your crediting logic here
}
echo "ok";http://yourdomain.com/system/gateways/offerwallapi.php| offer_type |
| Type of the completed offer: ptc, video, shortlink, push, microtask (task submissions carry a task:-prefixed transId) — or test for integration-test sends. |
| microtask |
| reward | Amount of virtual currency to credit. | 1.25 |
| reward_name | Your currency name as set in app settings. | Points |
| reward_value | Units of your currency per $1 USD (exchange rate). | 1000.00 |
| payout | USD payout value of the offer. | 0.100000 |
| userIp | User's IP address at time of completion. | 192.168.1.0 |
| country | User's country in ISO2 format. | US |
| status | 1 = credit, 2 = chargeback. | 1 |
| debug | 0 on live postbacks, 1 on test postbacks sent from the Integration test tool. | 0 |
| signature | MD5 hash for signature verification (legacy, kept for compatibility). | 17b4e2a70d6efe9796dd4c5507a9f9ab |
| signature2 | HMAC-SHA256 of the same concatenation with your Secret Key — recommended verification. | 8f14e45fceea167a5a36dedd4bea2543… |
reward and payout are always absolute values. Use the status field to determine whether to add or subtract.Always verify a signature parameter to confirm the postback originates from OfferwallApi servers. Two signatures are sent on every delivery — verify either one:
// Legacy (faucet-network standard):
signature = md5(subId . transId . reward . secretKey)
// Recommended (stronger, sent since v4.2):
signature2 = hash_hmac('sha256', subId . transId . reward, secretKey)You can find your Secret Key in the Apps & Websites section of the publisher cabinet — open your app details to reveal it.
<?php
$secret = ""; // Your Secret Key from OfferwallApi
$subId = $_REQUEST['subId'] ?? null;
$transId = $_REQUEST['transId'] ?? null;
$reward = $_REQUEST['reward'] ?? null;
$signature = $_REQUEST['signature'] ?? null;
$signature2 = $_REQUEST['signature2'] ?? null;
// Recommended: HMAC-SHA256 (stronger)
$expected2 = hash_hmac('sha256', $subId . $transId . $reward, $secret);
if (!hash_equals($expected2, (string)$signature2)) {
// Fallback / older integrations: MD5
$expected = md5($subId . $transId . $reward . $secret);
if (!hash_equals($expected, (string)$signature)) {
echo "ERROR: Signature doesn't match";
exit;
}
}
?>Postbacks are sent from our server’s outbound IP. Do not hardcode an address from this page — the current outbound IP is always shown in the IP Whitelist card on your publisher dashboard, and is also available machine-to-machine from the public brand endpoint:
curl -X GET "https://offerwallapi.com/api/public/brand"
{ "brandName": "…", "outboundIp": "203.0.113.10", … }Read the outboundIp field from that endpoint (or copy it from the dashboard) and whitelist it on your side if your firewall requires it.
transId against your database to prevent duplicate credits.Your endpoint must respond with ok — the body is trimmed and compared case-insensitively, but replying with exactly ok (lowercase, no extra whitespace) is the safest. Anything else marks the postback as failed — even if your processing was successful.
echo "ok"; // Required — must be the only outputA failed delivery is retried automatically with a growing backoff: 1 minute → 5 minutes → 15 minutes → 1 hour → 6 hours → 24 hours (6 attempts in total). Every attempt is logged with the HTTP status and your endpoint’s response — open the delivery history on your app page to inspect it, and use the re-send button there to retry an undelivered postback manually at any time.
Verify your endpoint before going live: publisher cabinet → Apps → Integration test panel of the application. It is available for apps in any status — including apps that are still pending moderation, so the integration can be checked while the app is being reviewed.
Choose a subId and a reward (in your currency units) and send the test: the panel shows the HTTP status, your endpoint’s response body and the exact signature that was sent, plus the full request URL with all parameters. The test postback carries debug=1 so you can tell it apart from live traffic.
test- prefixed transId and never create real earnings — safe to send at any integration stage.<?php
$secret = ""; // Your Secret Key
// Optional: restrict to the OfferwallApi outbound IP — take the current value
// from the IP Whitelist card on your publisher dashboard or from
// GET /api/public/brand (field "outboundIp"); it can change over time.
$allowed_ips = ['YOUR_OUTBOUND_IP'];
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
if (!in_array($ip, $allowed_ips)) {
echo "ERROR: Invalid source";
exit;
}
// Read postback parameters
$userId = $_REQUEST['subId'] ?? null;
$transId = $_REQUEST['transId'] ?? null;
$reward = $_REQUEST['reward'] ?? null;
$rewardName = $_REQUEST['reward_name'] ?? null;
$offerName = $_REQUEST['offer_name'] ?? null;
$offerType = $_REQUEST['offer_type'] ?? null;
$payout = $_REQUEST['payout'] ?? null;
$userIp = $_REQUEST['userIp'] ?? "0.0.0.0";
$country = $_REQUEST['country'] ?? null;
$status = $_REQUEST['status'] ?? null;
$signature2 = $_REQUEST['signature2'] ?? null;
// Validate signature — HMAC-SHA256 (recommended)
if (!hash_equals(hash_hmac('sha256', $userId . $transId . $reward, $secret), (string)$signature2)) {
echo "ERROR: Signature doesn't match";
exit;
}
// Handle chargeback
if ($status == 2) {
$reward = -abs($reward);
}
// Prevent duplicates — check if transaction already exists
if (isNewTransaction($transId)) {
processTransaction($userId, $reward, $transId);
}
// else: already processed, skip silently
echo "ok"; // Important!
?>| Push Notification | $0.0001/view + $0.001/click | Broad awareness | $1 |
| PopUp | $0.0008 per unique visit | Landing pages, offers | $1 |
| Banner | $0.0015 per unique click (CPC) | Brand visibility | $1 |
| Sponsored | Flat rate per duration | Long-term brand presence | $7 (7 days) |
All-in-One bundles combine four formats in a single purchase — PTC views, PopUp clicks, Banner clicks and Push notifications — configured in one wizard and billed at the exact sum of the component units. See Advertising → AIO in the advertiser cabinet.
PTC (Paid-to-Click) shows your website or YouTube video to users who are paid a small reward to view it for a set duration. Your page opens in a new tab on the offerwall (standard “iframe” tier or premium “window” tier) and the user must stay for the full duration to receive their reward.
| Type | What it shows | Format |
|---|---|---|
| PTC | Your website URL | New tab (standard or premium window tier) |
| PTP (Paid-to-Promote) | Your website URL | Promoted alongside other PTC ads |
| Video Ad | YouTube video URL | Embedded YouTube player |
Price is determined by the ad pack (duration in seconds). The longer the view duration, the higher the price per click — but also higher quality traffic. Pricing is loaded from the live tariff configuration. Check Advertising Rates for current rates.
| Field | Required | Notes |
|---|---|---|
| Title | Yes | Min 5 chars. No emojis. |
| Description | Yes | 15–255 chars. No emojis. |
| Target URL / YouTube URL | Yes | Must be a valid URL. YouTube: must be watchable (extracted video ID). |
| Ad Pack | Yes | Determines duration + price per click. |
| Total Visits | Yes | Min: system config (advertise_min). |
| Daily Limit | Optional | Min 500 if set. Controls daily delivery pace. |
| Device Target | Yes | All / Mobile only / Desktop only. |
| Country Target | Optional | Target specific countries or all. |
Boost any PTC campaign for $1/day (PTC) or $1/day (Video) to receive priority placement and increased delivery speed.
Shortlink advertising integrates with external shortlink services (such as web1s.com and others) via API. Users click your shortened link, view a loading/timer page, and earn a reward. You set the reward per visitor and target country tiers.
Total Cost = Reward × Visitors × Tier Multiplier| Target Tier(s) | Fee | Multiplier | Example ($0.005 × 100 visits) |
|---|---|---|---|
| Tier 1 only | +115% | 2.15× | $1.075 |
| Tier 2 only | +40% | 1.40× | $0.700 |
| Tier 3 only | +15% | 1.15× | $0.575 |
| Tier 1 + 2 | +65% | 1.65× | $0.825 |
| Tier 1 + 3 | +50% | 1.50× | $0.750 |
| Tier 2 + 3 | +35% | 1.35× | $0.675 |
| Tier 1 + 2 + 3 | +15% | 1.15× | $0.575 |
| Setting | Value |
|---|---|
| Minimum visitors | 100 |
| Minimum reward per visit | $0.001 |
| Max daily limit | 25 per day |
| Max simultaneous campaigns (same shortlink) | 3 |
| Field | Required | Notes |
|---|---|---|
| Name | Yes | No emojis. |
| Shortlink Domain | Yes | The shortlink provider domain (e.g. web1s.com). API key validated on submit. |
| API Key / Password | Yes | Your shortlink service API token. Must be unique across all advertisers. |
| Reward per visit | Yes | Min $0.001. |
| Total visitors (limit) | Yes | Min 100. |
| Daily limit | Yes | Max 25/day. |
| Target tier(s) | Yes | Select at least one tier. |
| Block Russia (RU) | Optional | Exclude Russian traffic. |
Push notifications are delivered as browser notifications to users while they are browsing publisher sites, PTC pages, shortlinks, and faucets. They appear in the top-right corner of the browser with your title, description, and optional image.
| Action | Rate | Example ($1 budget) |
|---|---|---|
| View (impression) | $0.0001 per view | ~9,000 views |
| Click | $0.001 per click | ~100 clicks |
Formula: (views × $0.0001) + (clicks × $0.001) = total spend| Field | Required | Notes |
|---|---|---|
| Title | Yes | Min 5 chars. No emojis. |
| Description | Yes | 15–255 chars. No emojis. |
| Target URL | Yes | Valid HTTP/HTTPS URL. |
| Budget (Funds) | Yes | Min $1. Deducted from Purchase Balance. |
| Icon Image | Optional | 100×100px, max 500KB. |
| Country Targeting | Optional | Target specific countries or global. |
| Coupon | Optional | Achievement coupon code for bonus budget. |
PopUp ads are full-page interstitial advertisements shown to users across the publisher network (5,000+ approved faucet and GPT sites). They are triggered once per unique visitor session, making them ideal for landing pages and high-impact offers.
| Property | Value |
|---|---|
| Billing | Per unique visit only |
| Price per visit (PPV) | $0.0008 |
| Price per 1,000 visits (CPM) | $0.80 |
| Minimum budget | $1 (~1,250 unique visits) |
| $10 budget | ~12,500 unique visits |
| Network reach | 5,000+ active publisher sites |
| Field | Required | Notes |
|---|---|---|
| Title | Yes | Min 5 chars. |
| Target URL | Yes | Valid URL. The page that opens in the popup. |
| Budget (Funds) | Yes | Min $1. |
| Country Targeting | Optional | Global or specific countries. |
Sponsored ads are featured listings that give your brand persistent visibility across high-traffic pages of the platform. Unlike performance ads, sponsored ads are time-based — you pay for a fixed duration and your listing remains active throughout.
| Duration | Price | Daily rate |
|---|---|---|
| 7 days | $7 | $1.00/day |
| 30 days | $25 | $0.83/day |
| 90 days | $70 | $0.78/day |
| 180 days | $130 | $0.72/day |
| 365 days | $240 | $0.66/day |
| Field | Required | Notes |
|---|---|---|
| Campaign Name | Yes | 5–32 chars. |
| Short Description | Yes | Displayed as the ad subtitle. |
| Long Description | Optional | Full page content (rich text editor). |
| Target URL | Yes | Valid URL. |
| Banner Image | Yes | Max 600KB. |
| Duration Plan | Yes | 1w / 1m / 3m / 6m / 1y. |
| Social Links | Optional | Facebook, Twitter, Instagram, LinkedIn, YouTube, Telegram. |
| Description |
|---|
| statusrequired | integer | Status |
| messagerequired | string | Message |
| … | Optional | Varies — endpoint-specific data |
Every coin active on FaucetPay is available for payouts.
Limits are applied per faucet api_key and keep both your integration and our network healthy.
| Limit | Value | Notes |
|---|---|---|
| Live keys | 60 / min | Per-faucet api_key. Burst up to 120 tolerated. |
| Burst tolerance | 2 seconds | Short bursts above the limit are tolerated for up to 2 seconds. |
A short, opinionated checklist. Each item maps to a class of incident seen in the wild.
Send your api_key with every request. Pass your faucet’s api_key in the api_key field of every POST body.
Send your first payout in under a minute. Replace YOUR_API_KEY with a real faucet key, target a test user (your own email works) and fire.
curl -X POST https://faucetpay.io/api/v1/send \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY&amount=100&[email protected]¤cy=BTC"Pay out cryptocurrency from your account balance to a FaucetPay user.
| Parameter | Type | Description |
|---|---|---|
| api_keyrequired | string | Your faucet's API key. |
| amountrequired | integer | Amount in the coin's smallest unit (satoshis for BTC). |
| torequired | string | Destination: email, username, or wallet address. |
| currencyrequired | string | Upper-case coin symbol, e.g. BTC, DOGE, USDT. |
| ip_addressoptional | string | Claimer IP — strongly recommended; enables anti-abuse rate-limiting. |
| referraloptional | string | Referral tag attached to this payout for your own reporting. |
curl -X POST https://faucetpay.io/api/v1/send \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY&amount=100&[email protected]¤cy=BTC&ip_address=203.0.113.4"{
"status": 200,
"message": "OK",
"rate_limit_remaining": 0.00049900,
"currency": "BTC",
"balance": 49900,
"balance_bitcoin": 0.00049900,
"payout_id": 12834721,
"payout_user_hash": "3f9c1a2e6b7d0f5e8c4a9b2d1f6e0c8a7b5d2e91"
}Verify that a destination is a registered FaucetPay user for the chosen currency.
| Parameter | Type | Description |
|---|---|---|
| api_keyrequired | string | Your faucet's API key. |
| addressrequired | string | Email, username, or wallet address to verify. |
| currencyrequired | string | Upper-case coin symbol to check membership under. |
curl -X POST https://faucetpay.io/api/v1/checkaddress \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY&[email protected]¤cy=BTC"{
"status": 200,
"message": "This address belongs to a FaucetPay user.",
"payout_user_hash": "3f9c1a2e6b7d0f5e8c4a9b2d1f6e0c8a7b5d2e91"
}Fetch your current faucet balance for a given coin.
| Parameter | Type | Description |
|---|---|---|
| api_keyrequired | string | Your faucet's API key. |
| currencyrequired | string | Upper-case coin symbol. |
curl -X POST https://faucetpay.io/api/v1/getbalance \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY¤cy=BTC"{
"status": 200,
"message": "OK",
"currency": "BTC",
"balance": 49900,
"balance_bitcoin": 0.00049900
}Return your most recent payouts, newest first.
| Parameter | Type | Description |
|---|---|---|
| api_keyrequired | string | Your faucet's API key. |
| countoptional | integer | How many payouts to return (1–100, default 10). |
| currencyoptional | string | Filter to a single coin. Omit for all. |
curl -X POST https://faucetpay.io/api/v1/payouts \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY&count=5¤cy=BTC"{
"status": 200,
"message": "OK",
"rewards": [
{
"to": "[email protected]",
"amount": 100,
"date": "02-05-26 21:12:00 GMT"
}
]
}Return every currency currently active on FaucetPay.
| Parameter | Type | Description |
|---|---|---|
| api_keyrequired | string | Your faucet's API key. |
curl -X POST https://faucetpay.io/api/v1/currencies \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "api_key=YOUR_API_KEY"{
"status": 200,
"message": "OK",
"currencies": ["BTC", "ETH", "USDT", "LTC", "DOGE"],
"currencies_names": [
{ "name": "Bitcoin", "acronym": "BTC" },
{ "name": "Ethereum", "acronym": "ETH" },
{ "name": "Tether", "acronym": "USDT" },
{ "name": "Litecoin", "acronym": "LTC" },
{ "name": "Dogecoin", "acronym": "DOGE" }
]
}Every endpoint answers HTTP 200 and carries the real outcome in the response body’s status field. Branch on that — these are the only values v1 sends.
| Code | Tone | Meaning |
|---|---|---|
| 200 | Success | OK — request succeeded |
| 401 | Error | Access denied — your IP is not whitelisted for this faucet, or the faucet owner's account is suspended |
| 402 | Error | The faucet does not have enough balance for this payout |
| 403 | Error | Forbidden — invalid or missing api_key |
| 404 | Error | Unknown API method, or the request was not a POST |
| 405 | Error | Invalid amount — must be a positive integer in the coin's smallest unit |
| 410 | Error | Invalid currency — not a coin FaucetPay supports |
| 450 | Warning | The send limit you set on your own faucet has been reached — try again later |
| 456 | Error | The recipient is not payable — no user owns that address, or the account is suspended or unverified |
| 457 | Error | The recipient was refused by your own rules — blacklisted, or failed your anti-fraud checks |
| 459 | Warning | Rate limited — too many API requests from your IP |
Scoped, revocable keys with a Bearer token. The v2 API is the modern surface for automation. Instead of one all-powerful faucet key, you mint narrow keys (read / send / manage / admin), send them as a Bearer token, and get a consistent JSON envelope. The legacy /api/v1 above is unchanged.
https://faucetpay.io/api/v2curl -X POST https://faucetpay.io/api/v2/balances \
-H "Authorization: Bearer YOUR_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{}'Mint scoped keys from your faucet’s Manage page. Each key is shown once, stored hashed, and can carry a per-key IP whitelist and (for send) a daily USD cap.
Read balances, payouts, statistics, currencies, settings, and anti-fraud status.
Make payouts — moves real funds. Keep server-side only.
Change faucet settings, rate limits, IP whitelist, and anti-fraud rules.
Create a faucet and request listing approval. Cannot delete faucets.
| Endpoint | Scope | Body | Description |
|---|---|---|---|
| /balance | read | currency? | Check your faucet balance for a coin. |
| /balances | read | — | List balances across all coins. |
| /currencies | read | — | List supported currencies. |
| /check-address | read | address | Verify a destination address. |
| /payouts | read | currency?, count? | List recent payouts. |
| /faucet | read | — | Get faucet details. |
| /stats/daily |
| Endpoint | Scope | Body | Description |
|---|---|---|---|
| /faucet/update | manage | faucet_name, faucet_domain, faucet_url, faucet_description, timer_minutes, currencies_selected?, categories_selected? | Update faucet settings. |
| /ratelimits/set | manage | ratelimits[] | Set rate limits. |
| /ip-whitelist | manage | — | IP whitelist. |
| /ip-whitelist/set | manage | ip_whitelist | Update IP whitelist. |
| /low-balance-notification/toggle | manage | — | Toggle low-balance alert. |
| Endpoint | Scope | Body | Description |
|---|---|---|---|
| /anti-fraud/rules | manage | — | Anti-fraud rules. |
| /anti-fraud/toggle | manage | — | Toggle anti-fraud. |
| /anti-fraud/rules/update | manage | trust_rank, negative_rank?, whitelist?, blacklist? | Update anti-fraud rules. |
| Endpoint | Scope | Body | Description |
|---|---|---|---|
| /send | send | idempotency_key, currency, amount, to, ip_address?, referral? | Send payout. |
| Endpoint | Scope | Body | Description |
|---|---|---|---|
| /faucet/create | admin | faucet_name, faucet_domain, faucet_url | Create faucet. |
| /approval-cost | admin | coin | Listing approval cost. |
| /faucet/request-approval | admin | coin | Request listing approval. |
The v2 send is the safe way to pay out: it requires an idempotency key and honors an optional per-key daily USD cap. It uses the same anti-fraud / balance / rate-limit path as the legacy send.
| Parameter | Type | Description |
|---|---|---|
| idempotency_keyrequired | string | Unique per logical payout. A retry with the same key never double-pays. |
| torequired | string | Recipient: email, username, or wallet address. |
| amountrequired | integer | Amount in the coin's smallest unit (e.g. satoshis for BTC). |
| currencyrequired | string | Upper-case coin symbol, e.g. BTC, DOGE. |
| ip_addressoptional | string | Recipient IP — recommended for anti-fraud. |
curl -X POST https://faucetpay.io/api/v2/send \
-H "Authorization: Bearer YOUR_SCOPED_KEY" \
-H "Content-Type: application/json" \
-d '{"idempotency_key":"claim-8f3a1c2e","to":"[email protected]","amount":100,"currency":"BTC","ip_address":"203.0.113.4"}'{
"success": true,
"message": "OK",
"data": {
"rate_limit_remaining": 0.00049900,
"currency": "BTC",
"balance": 49900,
"balance_bitcoin": 0.00049900,
"payout_id": 12834721,
"payout_user_hash": "3f9c1a2e6b7d0f5e8c4a9b2d1f6e0c8a7b5d2e91"
}
}Subscribe to payout events and receive HMAC-signed POSTs. Configure them from your faucet’s Manage page — webhook management is session + 2FA only, so a scoped key can never register a delivery endpoint.
POST https://your-site.example/webhooks/faucetpay
X-FaucetPay-Signature: sha256=4b0c…e91
{
"id": "9f2c1a…",
"event": "payout.sent",
"faucet_id": 1234,
"created_at": 1717365120,
"data": {
"to": "[email protected]",
"amount": 100,
"currency": "BTC",
"payout_id": "payout_9nq0xk2l",
"payout_user_hash": "3f9c…",
"message": "Payout completed successfully!"
}
}Verify the signature (Node):
import crypto from 'node:crypto';
// rawBody = the exact bytes you received (verify BEFORE JSON.parse)
const signature = req.headers['x-faucetpay-signature']; // 'sha256=<hex>'
const expected =
'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET).update(rawBody).digest('hex');
const ok =
signature &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!ok) return res.status(401).end();v2 uses standard HTTP status codes plus a descriptive message in the envelope.
| Code | Tone | Meaning |
|---|---|---|
| 200 | Success | 200 OK |
| 400 | Warning | 400 Bad request |
| 401 | Error | 401 Unauthorized |
| 403 | Error | 403 Forbidden |
| 409 | Warning | 409 Conflict |
| 429 | Warning | 429 Too many requests |
The FaucetPay MCP server lets an AI assistant (Cursor, Claude Code, Windsurf, …) read your faucet and tune settings over the Model Context Protocol. It’s a thin client over the v2 API — read + manage only.
Connect & configure — example for Cursor:
// ~/.cursor/mcp.json
{
"mcpServers": {
"faucetpay": {
"url": "https://mcp.faucetpay.io/mcp",
"headers": { "Authorization": "Bearer fpk_your_read_or_manage_key" }
}
}
}| Parameter | Type | Description |
|---|---|---|
| urlrequired | string | MCP server URL. |
| Authorizationrequired | header | Scoped API key (read or manage scope). |
Read tools: get_faucet, get_balances, get_balance, get_payouts, get_currencies, check_address, get_daily_stats, get_user_stats, get_transactions, get_ratelimits, get_low_balance_notification.
Manage tools: get_ip_whitelist, set_ip_whitelist, get_anti_fraud, toggle_anti_fraud, update_anti_fraud_rules, update_faucet_settings, set_ratelimits, toggle_low_balance_notification.
Prompts & resources: slash-style prompts faucetpay_security_review and faucetpay_monetization_setup ship with the server for on-demand help.
The Merchant API lets any website accept payments from FaucetPay users through a hosted checkout page. The buyer pays from their FaucetPay balance and the funds settle to your account instantly — no keys or server-side SDK required to get started.
Checkout starts with a simple HTML form POSTed to the FaucetPay checkout endpoint. The buyer is taken to a FaucetPay-hosted page to review and confirm the payment. Submit the form with a standard browser POST (not XHR) — the buyer must land on the hosted checkout page to confirm the payment.
POST https://faucetpay.io/merchant/webscr| Parameter | Type | Description |
|---|---|---|
| merchant_usernamerequired | string | Your FaucetPay username — the account that receives the payment. |
| item_descriptionrequired | string | Description of the item or service the buyer is paying for. |
| amount1required | string | Amount you want to receive, denominated in currency1. |
| currency1required | string | The pricing currency of your store (e.g. USDT, BTC, …). |
| currency2optional | string | The coin the buyer must pay with. Leave blank to let the buyer choose any supported coin. |
| customoptional | string | An identifier passed back on the callback — use it for your order ID or user ID. |
| callback_urloptional |
<form action="https://faucetpay.io/merchant/webscr" method="post">
<input type="hidden" name="merchant_username" value="YOUR_USERNAME">
<input type="hidden" name="item_description" value="PlayStation 5">
<input type="hidden" name="amount1" value="100">
<input type="hidden" name="currency1" value="USDT">
<input type="hidden" name="currency2" value="">
<input type="hidden" name="custom" value="order-4564211">
<input type="hidden" name="callback_url" value="https://your-site.com/ipn">
<input type="hidden" name="success_url" value="https://your-site.com/success">
<input type="hidden" name="cancel_url" value="https://your-site.com/cancel">
<input type="submit" name="submit" value="Pay with FaucetPay">
</form>After a completed payment, FaucetPay POSTs a form-encoded callback to your callback_url with the payment details and a single-use verification token. Verify the token server-side before delivering the goods.
POST https://your-site.com/ipn
Content-Type: application/x-www-form-urlencoded
token=1a2b3c4d5e6f7a8b9c0d
&transaction_id=87654321
&merchant_username=your_username
&payer_username=buyer_username
&amount1=100
¤cy1=USDT
&amount2=0.00105
¤cy2=BTC
&custom=order-4564211
&exchange_rate=95238.09Verify the token:
GET https://faucetpay.io/merchant/get-payment/{token}
{
"valid": true,
"transaction_id": "87654321",
"merchant_username": "your_username",
"payer_username": "buyer_username",
"amount1": "100",
"currency1": "USDT",
"amount2": "0.00105",
"currency2": "BTC",
"custom": "order-4564211"
}We expect that you respond to the callback request with an HTTP 200 OK response. If we don’t receive a 200 response, our system will assume that the request has failed and will reattempt the callback based on the following schedule.
| Attempt | Delay |
|---|---|
| 1st Callback | Immediately (after payment) |
| 2nd Callback | 5 minutes delay |
| 3rd Callback | 15 minutes delay |
| 4th Callback | 30 minutes delay |
| 5th Callback | 60 minutes delay |
| 6th Callback | 120 minutes delay |
| 7th Callback | 240 minutes delay |
| reward | string | User-facing reward in your currency: your share of the advertiser reward, converted at your exchange rate. |
| reward_usd | number | Your share of the advertiser reward, in USD. |
| status | string | available — the user may (re)submit a proof — or pending — a proof is awaiting review (carries submittedAt; resubmission answers 409). A REJECTED proof flips the task back to available, so the user can fix and retry. |
| submittedAt | string | ISO timestamp of the pending submission — only present when status is pending. |
| approved | string[] | Top-level array (next to data) of task ids this user already has an APPROVED submission for — hide them in your UI, the user is done with them. |
| url | null | Always null — microtasks have no hosted page; your UI is the task page. |
| This task has reached its daily limit. Come back tomorrow. |
| The advertiser-defined daily submission quota is exhausted for today. |
| 429 | Too many submissions. Try again later | Submission rate limit: 60/min per API key, 20/hour per app + end user (shared with the embedded wall). |
| 429 | You have too many submissions awaiting review… | Per-user cap on simultaneous PENDING submissions reached (platform default 3). |
task — a microtask: carries proofType / proofHint / targetUrl and no timer — submit the proof through the task endpoint (see the Microtasks section below).| The start request came from a VPN/proxy/datacenter IP while VPN blocking is enabled. |
| 404 | Offer unavailable / Campaign unavailable / Campaign budget exhausted | Unknown id, paused campaign, wrong campaign type, or exhausted budget. |
| 409 | You have already completed this ad today… | Duplicate-completion pre-check: this user (IP or Sub ID) already completed the ad today. |
| 422 | View duration not completed yet… / The ad tab was not kept open long enough… / The video was not watched to the end… | Claim sent too early, the ad tab was not kept open for ≥70% of the duration, or the video playback time is insufficient. |
| 429 | Too many requests. Try again in 42s | Per-IP rate limit on the open endpoints — back off and retry. |
URL, TEXT or IMAGE_URL — the shape of the proof the user must submit (see proof validation below).| proofHint | string | What the advertiser requires as proof (e.g. “link to your comment”). |
| targetUrl | string | The task page — open it in a new tab when the user accepts the task. |
| duration adMode url | null | Reserved for timed ads — always null for microtasks (no timer, no click link). |
| source | string | Always microtask. |
| 409 | You have already submitted this task… | Duplicate: this user already has a PENDING or APPROVED submission for the task (body carries duplicate: true). |
| 409 | This task has reached its daily limit. Come back tomorrow. | The advertiser-defined daily submission quota is exhausted for today. |
| 429 | Too many requests. Try again in 42s / Too many submissions. Try again later. | Per-IP (10/min) or per app + Sub ID (20/hour) submission rate limit. |
| 429 | You have too many submissions awaiting review… | Per-user cap on simultaneous PENDING submissions reached. |
| New Tab / Window | Optional | +10% price. Opens in new window instead of iframe. |
| Allow PTP | Optional | Enables your campaign to also appear as PTP. |
| read |
| — |
| Daily statistics. |
| /stats/users | read | coin, page | User statistics. |
| /transactions | read | coin, page | Transactions. |
| /ratelimits | read | — | Rate limits. |
| /low-balance-notification | read | — | Low-balance alerts. |
| string |
| Referral tag for your own reporting. |
| string |
| URL that receives the server-to-server POST callback once the payment completes. |
| success_urloptional | string | URL the buyer is redirected to after a successful payment. |
| cancel_urloptional | string | URL the buyer is redirected to if they cancel. |