# SERP Organic Result Extractor (`junipr/serp-organic-result-extractor`) Actor

Extract organic search-result entries from supplied SERP HTML or public query runs, preserving rank, title, URL, domain, snippet, sitelinks, and source evidence.

- **URL**: https://apify.com/junipr/serp-organic-result-extractor.md
- **Developed by:** [junipr](https://apify.com/junipr) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 query snapshot scanneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## SERP Organic Result Extractor

Extract organic search-result entries from supplied SERP HTML or public query runs, preserving rank, title, URL, domain, snippet, sitelinks, and source...

### Who it is for

SERP Organic Result Extractor helps seo agencies, content teams, market researchers, founders validating search demand extract organic search-result entries from supplied SERP HTML or public query runs, preserving rank, title, URL, domain, snippet, sitelinks, and source evidence. It produces schema-valid dataset rows, evidence-backed KVS reports, safe diagnostics, and capped pay-per-event pricing for predictable Apify Store runs.

Turn captured search pages into clean organic-result rows for rank tracking, domain research, snippet analysis, and result-set comparisons. Ads, People Also Ask blocks, local packs, shopping modules, and other feature cards are excluded from the organic stream.

### Evidence contract

The actor emits only facts visible in supplied HTML, explicit input records, or a permitted public URL response. It never creates rankings, questions, modules, suggestions, titles, snippets, or page metadata to fill an evidence gap. A missing source produces a schema-valid status row explaining what input is needed.

Network access is conservative. Only HTTP and HTTPS URLs are accepted; localhost, loopback addresses, private IPv4 ranges, and disallowed domains are rejected. The runtime does not follow links discovered in a page. Challenge and access-denied pages are recorded as blocked evidence and are not solved or circumvented.

### Input modes

- Pass `serpHtmlInputs` with a query and captured HTML for deterministic extraction.
- Pass `htmlInputs` when your collection system already stores page HTML.
- Pass a public snapshot URL in `startUrls`; optional `allowedDomains` limits where the actor may connect.
- Pass `queries` without HTML to receive an explicit source-required status row, never invented rankings.

### Example input

```json
{
  "queries": [
    "best workflow software"
  ],
  "serpHtmlInputs": [
    {
      "sourceUrl": "https://example.com/serp",
      "query": "best workflow software",
      "html": "<article class=\"organic-result\" data-rank=\"1\"><a href=\"https://vendor.example/workflow\">Workflow Platform</a><cite>vendor.example</cite><p class=\"snippet\">Automate recurring work.</p></article>"
    }
  ],
  "maxResultsPerQuery": 10,
  "maxSnapshots": 1,
  "maxChargeUsd": 50
}
```

### Dataset output

Each dataset item is one JSON object, which makes the result directly usable in Apify integrations, exports, webhooks, and downstream data pipelines.

- `resultPosition`, normalized from visible `data-position` or `data-rank` evidence
- `title`, `resultUrl`, `displayedUrl`, and `domain`
- visible `snippet` text and optional `sitelinks`
- query, search-engine label, snapshot key, HTTP status, and blocked status

Common evidence fields include `sourceUrl`, `sourceType`, `pageTitle`, `issueCode`, `severity`, `evidenceSnippet`, `recommendation`, `httpStatus`, and `scannedAt`. Fields without source evidence are null rather than guessed.

### Reports and retained evidence

When `includeReport` is true, the actor writes a Markdown summary, CSV records, JSON issue rows, evidence excerpts, and run metadata to key-value storage. Set `includeRawSnapshots` only when retaining capped HTML is appropriate for your workflow; it is off by default.

The report metadata records the actor, timestamp, row count, issue codes, severity totals, consumed input fields, pricing mode, platform-usage setting, and maximum-charge default. This provides a compact audit trail for scheduled comparisons.

### Pricing

This actor uses pay per event. Apify platform-usage pass-through is off. Charges are accepted before the corresponding paid dataset row or report is written.

| Event | Price |
| --- | ---: |
| `actor-start` | $0.08000 |
| `query-snapshot-scanned` | $0.01000 |
| `live-serp-page-rendered` | $0.02750 |
| `serp-result-record-extracted` | $0.00950 |
| `report-generated` | $0.18000 |

`maxChargeUsd` is a strict pre-output budget. A value of zero prevents the first charge and therefore prevents paid output. If Apify rejects a charge, partially accepts it, or reports an event limit, the actor stops before writing the affected output and records billing status.

Higher-tier Store discounts are expected where supported. The event prices above remain the locked base prices.

### Limits and predictable runs

- `maxPages`, `maxSnapshots`, and actor-specific record caps limit work before analysis.
- `maxQueries` limits distinct search contexts.
- Public fetches are additionally constrained by `allowedDomains`, `timeoutMs`, `requestDelayMs`, and a fetch cap.
- Reports are optional and raw HTML retention is opt-in.
- Query-only input produces an explicit source-required row instead of an empty or fabricated result.
- Dataset rows and report files follow the declared Actor schemas.

### Responsible use

Process only public content or material you are authorized to analyze. Respect website terms, rate limits, privacy obligations, intellectual-property rights, and applicable law. Avoid sending personal or confidential information in HTML or input records unless your workflow is authorized to process it.

# Actor input Schema

## `startUrls` (type: `array`):

Optional explicit public URLs. Private, loopback, and non-HTTP targets are rejected.

## `sitemapUrls` (type: `array`):

Optional public XML sitemap URLs constrained by allowedDomains.

## `htmlInputs` (type: `array`):

Deterministic HTML objects with sourceId, sourceUrl, html/currentHtml, and optional query.

## `allowedDomains` (type: `array`):

Optional hostname allowlist for network fetches.

## `maxPages` (type: `integer`):

Overall hard cap on HTML targets processed.

## `maxDepth` (type: `integer`):

Reserved at zero: this runtime does not follow discovered links.

## `includeEvidence` (type: `boolean`):

Include visible evidence snippets in dataset rows.

## `includeRawSnapshots` (type: `boolean`):

Store capped supplied/fetched HTML in key-value storage.

## `requestDelayMs` (type: `integer`):

Delay between permitted fetches.

## `timeoutMs` (type: `integer`):

Per-request timeout in milliseconds.

## `userAgentMode` (type: `string`):

Use the standard actor agent or a compatibility browser-style agent.

## `queries` (type: `array`):

Queries associated with supplied snapshots.

## `searchEngine` (type: `string`):

Source engine label attached to rows.

## `countryCode` (type: `string`):

ISO country context for the supplied snapshot.

## `languageCode` (type: `string`):

Language context for the supplied snapshot.

## `deviceProfile` (type: `string`):

Device context for the supplied snapshot.

## `serpHtmlInputs` (type: `array`):

Captured SERP HTML objects with optional query, sourceUrl, and searchEngine.

## `maxQueries` (type: `integer`):

Maximum distinct queries processed.

## `maxSnapshots` (type: `integer`):

Maximum supplied or fetched snapshots processed.

## `maxBrowserPages` (type: `integer`):

Hard cap on explicitly supplied public snapshot URLs fetched.

## `maxResultsPerQuery` (type: `integer`):

Maximum visible organic records emitted per query.

## `includeSitelinks` (type: `boolean`):

Preserve visible secondary links inside result cards.

## `maxChargeUsd` (type: `number`):

Maximum accepted PPE spend before the actor stops without emitting unpaid output.

## `includeReport` (type: `boolean`):

Write Markdown, CSV, JSON evidence, and run metadata to key-value storage.

## Actor input object example

```json
{
  "startUrls": [],
  "sitemapUrls": [],
  "htmlInputs": [],
  "allowedDomains": [],
  "maxPages": 1,
  "maxDepth": 0,
  "includeEvidence": true,
  "includeRawSnapshots": false,
  "requestDelayMs": 250,
  "timeoutMs": 15000,
  "userAgentMode": "standard",
  "queries": [
    "best project management software"
  ],
  "searchEngine": "google",
  "countryCode": "US",
  "languageCode": "en",
  "deviceProfile": "desktop",
  "serpHtmlInputs": [],
  "maxQueries": 1,
  "maxSnapshots": 1,
  "maxBrowserPages": 1,
  "maxResultsPerQuery": 10,
  "includeSitelinks": true,
  "maxChargeUsd": 50,
  "includeReport": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `report` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("junipr/serp-organic-result-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("junipr/serp-organic-result-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call junipr/serp-organic-result-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,junipr/serp-organic-result-extractor"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/TpfJuVmUsaZVsLftY/builds/g6Rgr7JQZZw00fyq7/openapi.json
