Try it
Run a live Deep Search request
One real call against the API, no signup and no key. Flip Pixel positions on and run it.
What an anonymous run shows you. Anonymous calls are served from the free allowance and the pixel fields are a paid feature, so the search runs normally and metadata.pixelPositionUnavailable comes back as free_tier in place of the two fields. On a key of your own the same request adds pixel_position and pixel_box alongside the ordinary fields, and nothing else about the response changes. Both keys are optional — an item deep in the response can arrive without them, so branch on whether the field is there.
Why it matters
Rank counts results. Nobody scrolls a count.
A rank number answers “how many organic results are ahead of me?”, never “how far down the page am I?” Here is one keyword's page, top to bottom, with the pixel_position of each block.
ads.top[0]sponsored · 82 px tall
ads.top[1]sponsored · 82 px tall
ads.top[2]sponsored · 82 px tall
aiOverview428 px tall — starts above the fold, ends below it
peopleAlsoAsk[0…3]four rows, 52 px each
position: 1this is you
organic[1]position: 2
Rank #1, one and a half screens down
Three ads, an AI Overview and a People Also Ask block sit above the first organic result on this page. Its position is 1. Its pixel_position is 1082 — and the viewport it is measured against is 768 px tall.
That is the number your rank report cannot show you, and it is the number that explains the traffic. Nothing about the page changed the ranking — it changed the distance. When the AI Overview grows by 200 px next month, rank stays at 1 and the distance grows to 1,282. Only pixel position moves.
Note the AI Overview row: it starts at 398, above the fold, and is 428 px tall, so it ends at 826 — below it. pixel_position is the element's top edge; pixel_box.h is what tells you it crosses the fold rather than fitting inside the first screen.
The stack above is a worked example, drawn to show how the fields fit together and how the arithmetic runs — not a measurement of any one live page. Run the call on your own keyword and the response gives you the same fields with your numbers in them.
The gap
Two keywords. Both “rank 3”. One report.
This is the reason the field exists. An ordinal rank collapses two completely different commercial outcomes into the same cell of a spreadsheet. A pixel value pulls them apart, and it does it with one integer you can sort, threshold and chart like any other metric.
"how to tie a tie"
- position
- 3
- pixel_position
- 402
- above it
- 2 organic rows
- fold (768 px)
- Above
"car insurance quotes"
- position
- 3
- pixel_position
- 1830
- above it
- 3 ads, AI Overview, PAA, 2 organic rows
- fold (768 px)
- 2.4 screens below
Both cards are worked examples using the response fields as they ship. The point is the shape of the comparison, not the two specific numbers — and the shape is what you can only build once every item in the response carries a coordinate.
Endpoints & Pricing
One flag on the search you already pay for
Pixel position is not a separate product with its own rate card. It is an opt-in parameter on Deep Search, billed in the Web category exactly like a call without it.
/api/search
- Accepts
pixel_position=true - Full SERP object, every feature block
- Up to 100 results / 10 pages per call
pixel_position+pixel_boxon every matched item- Paid calls only — free credit returns the marker instead
- Four engines:
google,yahoo,bing,ddg
/api/search/quick
- Organic results only, built for speed
- No pixel fields, on any engine
- The
pixel_positionflag is accepted, never an error - Returns
metadata.pixelPositionUnavailable - …and a
pixelPositionHintnaming/api/search - Use it for ordinal rank; use Deep Search when you need pixels
Growth pricing applies from $100 deposited, Scale from $500. New accounts get free calls shared across every free-eligible endpoint — those are free-tier calls, so they return the free_tier marker rather than pixel fields.
Engine support
Four engines carry pixel fields. Brave does not.
Read this table before you write the job, because the difference is silent: a Brave request with pixel_position=true is accepted and answers normally — it simply has no pixel fields on any item. If your code assumes the key is there, that is where it breaks.
| Engine | Deep Search /api/search |
Quick Search /api/search/quick |
What to read |
|---|---|---|---|
Google engine=google | Pixel fields | None | pixel_position, pixel_box |
Bing engine=bing | Pixel fields | None | pixel_position, pixel_box |
Yahoo engine=yahoo | Pixel fields | None | pixel_position, pixel_box |
DuckDuckGo engine=ddg | Pixel fields | None | pixel_position, pixel_box |
Brave engine=brave | None | None | the ordinal position field |
Deep Search on the four supported engines covers desktop results in the US locale, measured against a 1366 × 768 viewport. Mobile viewports are on the roadmap and are not available today — if your reporting is mobile-first, that is a real gap and you should know it before you buy. On Quick Search the parameter is accepted on every engine and no engine returns pixel fields; the response says so in metadata.pixelPositionUnavailable.
Quick Start
One flag, then two extra fields per item
Add pixel_position=true to a /api/search call. The request is otherwise unchanged, and a call without the flag returns exactly the bytes it returned before the feature existed.
curl "https://apiserpent.com/api/search?q=car+insurance+quotes&engine=google&country=us&pixel_position=true" \ -H "X-API-Key: YOUR_API_KEY"
import requests FOLD = 768 # the viewport height the page is measured against DOMAIN = "example.com" r = requests.get( "https://apiserpent.com/api/search", headers={"X-API-Key": "YOUR_API_KEY"}, params={"q": "car insurance quotes", "engine": "google", "country": "us", "pixel_position": "true"}, ).json()["results"] mine = next((o for o in r["organic"] if DOMAIN in o["url"]), None) y = mine and mine.get("pixel_position") # may be absent — never assume it print(f"rank {mine['position']}, opens at {y}px, {round(y / FOLD, 2)} screens down") # → rank 1, opens at 1082px, 1.41 screens down # Everything the SERP put above you, with the height each one takes. blocks = [] for key in ("aiOverview", "featuredSnippet", "knowledgePanel"): b = r.get(key) if b and b.get("pixel_position") is not None: blocks.append((key, b["pixel_position"], (b.get("pixel_box") or {}).get("h"))) for name, items in (("ad", (r.get("ads") or {}).get("top") or []), ("paa", r.get("peopleAlsoAsk") or [])): for it in items: if it.get("pixel_position") is not None: blocks.append((name, it["pixel_position"], (it.get("pixel_box") or {}).get("h"))) above = sorted(b for b in blocks if b[1] < y) print(len(above), "blocks above you,", sum(h or 0 for _, _, h in above), "px of them")
const FOLD = 768; const res = await fetch( "https://apiserpent.com/api/search?q=mortgage+rates+today&engine=google&country=us&pixel_position=true", { headers: { "X-API-Key": "YOUR_API_KEY" } } ); const { results } = await res.json(); // pixel_position is the TOP edge. A row is fully visible only if its box ends // above the fold too — that is what pixel_box.h is for. const visible = results.organic.filter(o => o.pixel_position != null && o.pixel_position + ((o.pixel_box && o.pixel_box.h) || 0) <= FOLD ); console.log(`${visible.length} organic results fit on the first screen`); // Share of the first screen taken by everything that is not an organic result. const area = b => (b && b.pixel_box) ? b.pixel_box.w * b.pixel_box.h : 0; const featureArea = area(results.aiOverview) + area(results.featuredSnippet) + (results.ads?.top || []).reduce((s, a) => s + area(a), 0); console.log("feature pixels above the fold:", featureArea);
{
"success": true,
"engine": "google",
"results": {
"organic": [
{
"position": 1, // ordinal — 1st organic result
"title": "…",
"url": "https://example.com",
"pixel_position": 1082, // y of the element's top edge
"pixel_box": { "x": 126, "y": 1082, "w": 652, "h": 118 }
} // pixel_box.y === pixel_position, always
],
"ads": {
"top": [
{ "title": "…", "pixel_position": 120, "pixel_box": { "x": 126, "y": 120, "w": 652, "h": 82 } }
]
},
"aiOverview": {
"text": "…",
"pixel_position": 398,
"pixel_box": { "x": 125, "y": 398, "w": 1100, "h": 428 }
}, // 398 + 428 = 826 → crosses the 768px fold
"peopleAlsoAsk": [
{ "question": "…", "pixel_position": 842, "pixel_box": { "x": 126, "y": 842, "w": 652, "h": 52 } }
],
"featuredSnippet": null, // not on this page — the key is still here
"knowledgePanel": null
}
}
// (a) the call was served from your free allowance { "metadata": { "pixelPositionUnavailable": "free_tier" } } // (b) you sent the flag to /api/search/quick { "metadata": { "pixelPositionUnavailable": "not_supported_on_quick", "pixelPositionHint": "Pixel positions are available on /api/search (Deep Search)." } } // Both still return the search itself. Branch on the marker, not on an error: // neither of these is a 4xx, and neither changes the rest of the response.
Spec
What you get, precisely
Two sibling fields on every item the measurement matched, in one consistent shape across every result type and every supported engine. No per-block variants, no source flags.
pixel_position=true on GET /api/search (Deep Search). Defaults to false; a response with the flag omitted is byte-identical to one from before the feature shipped.google, yahoo, bing and ddg. engine=brave returns no pixel fields — the request still succeeds, so read the ordinal position field there./api/search/quick accepts the flag on every engine and returns none of the fields; News, Images and Videos do not take the parameter at all.pixel_positionpixel_position < 768 starts on the first screen — use pixel_box.h to know whether it also finishes there.pixel_box{ x, y, w, h } — left offset, top offset, width and height, all integers in the same coordinate space. pixel_box.y equals pixel_position by construction. Use it for element area, centre point, fold crossing and share-of-voice scoring.organic[] row, plus aiOverview, featuredSnippet, knowledgePanel, peopleAlsoAsk[], ads.top[], localPack[], videos[], shopping[] and relatedSearches[] — on each of those blocks when the results page actually contains it.metadata.pixelPositionUnavailable: 'free_tier' and no pixel fields, so budget a pixel job against paid credit.position.Who buys this
What teams build with a coordinate
Every one of these needs a distance, not an ordinal. None of them can be built from a rank number alone.
Above-the-fold reporting
Threshold on pixel_position < 768 and you have a column no rank tracker ships: which of a client's keywords are actually on the first screen. Add pixel_box.h and you can separate “starts on screen” from “fits on screen”.
Explaining a flat-rank traffic drop
The hardest client conversation in SEO is “rank held, traffic fell”. Chart pixel_position next to rank and the answer is usually visible on the chart: the number stayed at 3 and the distance grew by 600 px.
Share of the first screen
pixel_box gives width and height, so it gives area. Sum the area every block occupies above 768 px and you can score a SERP by who owns the screen — ads, an AI Overview, or organic results.
SERP layout change detection
Feature rollouts move pixels long before they move rankings. Store the y of each block per keyword per day and a new AI Overview shows up as a step change in one column, dated, on the day it happened.
Snippet and AI Overview audits
Owning the featured snippet only pays if the snippet is near the top. Its pixel_position tells you whether it opens the page or sits under an AI Overview and three ads — two very different assets with the same name.
Ad placement checks
Ads carry the fields too. ads.top[] comes back with a y and a box, so a paid team can see where a placement actually landed on the page rather than inferring it from a position label.
Compare
Pixel geometry across the providers that ship it
Only a handful of SERP APIs publish rectangle coordinates at all, so the honest comparison is about coverage, surcharge and device — not about who has the feature.
| Capability | Serpent API | SerpApi.com | DataForSEO |
|---|---|---|---|
| Rectangle geometry on organic results | Yes — pixel_box | Yes | Yes — calculate_rectangles |
| Web-search engines it covers | Four — Google, Bing, Yahoo, DuckDuckGo | Google only (plus the Google Ads product) | Google only |
| Single-integer y alongside the box | Yes — pixel_position | Read it off the rectangle | Read it off the rectangle |
| Extra charge to switch it on | None — same per-call price | Included in the plan's search count | Yes — documented as increasing the task cost |
| Mobile viewport | Not yet — desktop 1366×768 only | Yes | Yes — configurable screen size |
| Entry price | $0.60/1K calls, $0.03/1K at Scale | $25/mo for 1,000 searches | Per-task, plus the rectangle surcharge |
| Free allowance | Free to start — free calls do not return pixel fields | 250 searches/month | Trial credits |
Competitor rows above were read from their own documentation on 7 September 2026; re-check them before quoting. A fourth vendor, Authoritas, also publishes element coordinates (“the (x,y) position of all elements on the page”, read 16 September 2026) but prices in pounds only, so it is not in this dollar table — see the full provider comparison, which covers all four and names the eight vendors that publish no geometry field at all. Mobile is the row we lose on today and we would rather you read it here than find it in an integration. See the full SERP API price comparison for everything outside the pixel feature.
FAQ
Pixel Position questions
pixel_position=true to a GET /api/search (Deep Search) call. Nothing else about the request changes, and a request without the flag is unchanged in every byte. Every item that the measurement matched then carries two sibling fields: pixel_position, an integer y-coordinate, and pixel_box, an object with x, y, w and h. Quick Search does not carry pixel fields on any engine: /api/search/quick accepts pixel_position=true, runs the search normally and returns metadata.pixelPositionUnavailable set to not_supported_on_quick plus a metadata.pixelPositionHint pointing at /api/search.
engine=google, engine=yahoo, engine=bing and engine=ddg, on desktop US results at a 1366×768 viewport. engine=brave does not return pixel fields. A Brave request with pixel_position=true is still accepted and still answers normally — it simply comes back without pixel_position or pixel_box on any item, so read the ordinal position field there or send the keyword to one of the other four engines. Mobile viewports are on the roadmap and are not available today.
pixel_position is one integer: the top edge of the element. pixel_box is the whole rectangle — { x, y, w, h } — where x and y are the top-left corner measured from the page origin and w and h are the element's width and height in pixels. pixel_box.y is the same number as pixel_position by construction. Reach for pixel_box whenever the top edge alone is not enough: a block's area (w times h) for share-of-voice scoring, its centre point, or whether it crosses the fold rather than merely starting above it. An AI Overview that starts at 398 px and is 428 px tall begins above the fold and ends below it, and only the box tells you that.
pixel_position=true is billed exactly like a web search without it — $0.60 per 1,000 calls on the default rate, $0.06 on Growth, $0.03 on Scale. There is no separate product to buy and no upgrade to make. The one requirement is that the call be a paid one: a request served out of your free allowance comes back with metadata.pixelPositionUnavailable set to free_tier and no pixel fields, so plan a pixel job against paid credit.
calculate_rectangles on a Google SERP Advanced task, and its help centre states plainly that enabling the parameter increases the cost of the task. Serpent returns pixel fields on four web-search engines rather than one, at $0.60 per 1,000 calls falling to $0.03, with no surcharge for the flag — and today only on desktop, which is the honest trade.
position field whenever pixel_position is absent, and read pixel_box with a null-check rather than assuming it: nothing about the response shape changes when a value cannot be produced, which is deliberate.
Stop reporting ordinals
Add pixel_position=true to a Google, Bing, Yahoo or DuckDuckGo /api/search call and every element comes back with the coordinate it renders at. Opt-in flag, BETA, no price increase — from $0.60 per 1,000 calls, $0.03 at Scale. Every account starts with free calls to try the endpoint; add credit when you want the pixel fields, because a free-tier call returns the free_tier marker instead.

