SEO traffic (CTR)
POST /v1/traffic/clicks runs real Google searches from genuine browsers, finds
your target URL in the results, and clicks it — then browses with natural dwell.
Use it to test organic CTR, dwell time, and search visibility. Billed $0.04
per delivered click.
Request
Section titled “Request”curl -X POST https://api.serplify.io/v1/traffic/clicks \ -H "Authorization: Bearer live_your_key_here" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme — running shoes cluster", "keywords": [ "best running shoes", "trail running shoes", "cheap running shoes" ], "target": "example.com", "quantity": 200, "geo": { "country": "US" }, "device": "mobile", "search_max_pages": 5, "dwell_seconds_min": 40, "dwell_seconds_max": 120, "pages_per_visit": 2 }'| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
keywords | string[] | object[] | yes* | — | Up to 50 queries in one campaign. Strings split quantity evenly; objects { keyword, quantity } set each share explicitly. |
keyword | string | yes* | — | Single query. Send this or keywords, never both. |
name | string | no | Clicks: <first keyword> | Campaign name as it appears in the dashboard. |
target | string | yes | — | URL/domain to find and click in the SERP. |
quantity | integer | yes | — | Clicks to deliver (1–1,000,000), across all keywords. Must be ≥ the number of keywords. |
geo | object | no | — | { country? } — ISO-3166 alpha-2, country-level targeting. |
device | enum | no | mixed | desktop, mobile, mixed. |
pacing | enum | no | asap | asap delivers as fast as capacity allows; distributed spreads clicks naturally over spread_hours. |
spread_hours | integer | no | 24 | For pacing: "distributed": spread the clicks over this many hours (1–720). |
search_max_pages | integer (1–10) | no | 5 | How deep into the SERP to look for and deliver the target. A shallow 1–3-page miss is not enough evidence for the automatic not-ranking guard. |
dwell_seconds_min / dwell_seconds_max | integer | no | 30 / 120 | Time on the clicked page. |
pages_per_visit | integer | no | 2 | Internal pages browsed after the click. |
tag | string | no | — | Free-form label. |
* Exactly one of keywords or keyword is required.
Daily campaign creation limit
Section titled “Daily campaign creation limit”An account may create up to 25 SEO traffic projects per UTC day by default.
The counter is shared by dashboard projects and Traffic API campaigns. Deleting
a project does not refund its creation slot; failed requests do not consume one.
Operators can change the runtime limit, and 0 disables rejection.
One campaign per URL, not per keyword
Section titled “One campaign per URL, not per keyword”If a page targets several queries, send them as keywords in a single request.
You get one campaign to name, watch and poll instead of one per keyword —
which matters at volume: 150 URLs × 6 keywords is 900 campaigns if you split
them, and 150 if you don’t.
quantity is the total across the campaign and is split evenly, remainder first,
so the parts always sum to what you bought. quantity: 200 over 3 keywords
gives 67 / 67 / 66.
Weighting keywords
Section titled “Weighting keywords”A head term and a long-tail term rarely deserve the same number of clicks, so you can set each share yourself:
{ "name": "Acme — running shoes cluster", "quantity": 100, "keywords": [ { "keyword": "best running shoes", "quantity": 70 }, { "keyword": "trail running shoes", "quantity": 20 }, { "keyword": "cheap running shoes", "quantity": 10 } ]}Set quantity on every keyword or on none — mixing the two is rejected,
because guessing a share for the unset ones would change what you bought for the
ones you did specify. Explicit shares must sum exactly to the campaign
quantity, so a campaign can never quietly under-deliver what you paid for.
Response
Section titled “Response”HTTP 202 with a campaign whose
product is clicks. A click is only counted (and billed) when the browser
actually finds the target within search_max_pages and clicks through — if the
target isn’t found on a given attempt, you are not charged for it.
Tracking progress per keyword
Section titled “Tracking progress per keyword”GET /v1/traffic/campaigns/{id} returns a keywords array alongside the
campaign totals:
{ "campaign_id": "9f1c…", "name": "Acme — running shoes cluster", "product": "clicks", "quantity": 200, "delivered": 118, "status": "running", "keywords": [ { "keyword": "best running shoes", "quantity": 67, "delivered": 67, "status": "completed" }, { "keyword": "trail running shoes", "quantity": 67, "delivered": 51, "status": "delivering" }, { "keyword": "cheap running shoes", "quantity": 66, "delivered": 0, "status": "not_ranking" } ]}| Keyword status | Meaning |
|---|---|
delivering | Working through its share of the quantity. |
completed | Delivered its full share. |
not_ranking | Repeated searches could not find your target within search_max_pages, so this keyword was switched off. It will never deliver — replace it, or raise search_max_pages. You were not charged for those searches. |
not_ranking is the one to watch if you generate keywords programmatically: it
is the difference between a campaign that is merely slow and one that can never
finish. A campaign whose remaining keywords are all not_ranking will stop
short of quantity, and the unspent balance stays on your account.
What you get
Section titled “What you get”- A genuine browser performs the search, so the click is a real organic SERP interaction (not a synthetic hit on your URL).
- Natural dwell and post-click browsing produce realistic engagement signals.
geo/deviceroute through matching residential/mobile proxies.
Responsible use
Section titled “Responsible use”This is a measurement and testing tool. Serplify does not guarantee ranking changes, and you must be authorised to drive traffic to the target. See the Acceptable Use policy.