Skip to content

Add native Extract API support - #37

Open
strzibny wants to merge 3 commits into
serpapi:masterfrom
strzibny:extract-api
Open

strzibny wants to merge 3 commits into
serpapi:masterfrom
strzibny:extract-api

Conversation

@strzibny

@strzibny strzibny commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Let's add first-class support for the Extract API (engine=extract), which turns any public web page into a structured JSON document and supports scrape (default), map, and crawl modes.

New methods

extract(params)

Runs an extraction. Forces engine: 'extract' and fails fast with SerpApiError when the required :url parameter is missing. All other parameters (mode, max_pages, max_depth, include_subdomains, include_sitemap, device, prefetch_delay, timeout, include_tags, exclude_tags, include_markdown, include_inline_assets, output) pass through verbatim, keeping the backend as the source of truth for validation. The hash-only signature matches the gem's existing convention (search(q: ...)) and mirrors the planned Python SDK shape (client.extract(url=...)).

client = SerpApi::Client.new(api_key: ENV['SERPAPI_KEY'])

results = client.extract(url: 'https://example.com/')
pp results[:page_information]

results = client.extract(url: 'https://books.toscrape.com/', mode: 'crawl', max_pages: 3, max_depth: 1)
pp results[:pages]

extract_link(link, params = {})

Follows a serpapi_extract_link embedded in Extract results (inline_links, breadcrumbs, price_offers, map urls, crawl pages). These links carry the original request's parameters but deliberately no api_key, so they can't be fetched raw. This method reconstructs the parameters from the link and re-issues the request through the client, applying the api_key, defaults, timeouts, and error handling. It is mainly useful when results are detached from the request that produced them (stored JSON, Search Archive, agent pipelines). Since these links flow out of scraped web content, it rejects anything that is not a serpapi.com URL targeting engine=extract.

results = client.extract(url: 'https://example.com/')
linked_page = client.extract_link(results[:inline_links].first[:serpapi_extract_link])

@strzibny
strzibny requested review from jvmvik and trusche October 14, 2026 07:44
Add first-class support for the Extract API (engine: extract), which
turns any public web page into a structured JSON document and supports
scrape (default), map, and crawl modes.

New methods on SerpApi::Client:

- extract(params): runs an extraction; forces engine=extract and
  fails fast when the required :url parameter is missing. All other
  parameters (mode, max_pages, max_depth, include_subdomains,
  include_sitemap, device, prefetch_delay, timeout, include_tags,
  exclude_tags, include_markdown, include_inline_assets, output)
  pass through verbatim - the backend remains the source of truth.

- extract_link(link, params = {}): follows a serpapi_extract_link
  embedded in Extract results (inline_links, breadcrumbs, price_offers,
  urls, pages). It reconstructs the original request parameters from
  the link and re-issues it through the client so the api_key, default
  params, timeouts, and error handling apply. Guards against non
  serpapi.com URLs and non-extract engines, since these links flow out
  of scraped web content.

Includes offline unit specs, live example specs, a runnable demo
(demo/demo_extract.rb), and README documentation.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Conflicting engine parameters and link-supplied credential overrides remain unresolved.

2 open findings
What changed in this PR

Adds native Extract API support through the existing client request flow.

Changes:

  • Adds extract and extract_link helpers.
  • Adds unit specifications and live examples for extraction modes.
  • Documents usage and updates the class-length limit.
File Description
spec/​serpapi/​client/​extract_api_spec.rb Tests helpers and validation.
spec/​serpapi/​client/​example/​example_extract_spec.rb Exercises scrape, map, and crawl.
README.md Documents extraction and link following.
lib/​serpapi/​client.rb Implements Extract API helpers.
lib/​serpapi.rb Loads URI parsing support.
demo/​demo_extract.rb Demonstrates modes and chained extraction.
CHANGELOG.md Records Extract API support.
.rubocop.yml Raises the class-length limit.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/serpapi/client.rb Outdated
.transform_keys(&:to_sym)
raise SerpApiError, "link does not target the extract engine: #{link}" if link_params[:engine] != 'extract'

search(link_params.merge(params))
Comment thread lib/serpapi/client.rb Outdated
raise SerpApiError, "params must be hash, not: #{params.class}" unless params.instance_of?(Hash)
raise SerpApiError, 'url parameter is required by the extract API' if params[:url].to_s.empty? && params['url'].to_s.empty?

search(params.merge(engine: 'extract'))
extract_link merged every parameter from the link, so a link carrying an
api_key (e.g. planted in scraped content or stored results) replaced the
client's configured key. Drop it so requests always use the client's
credentials.
extract forced engine: 'extract' with a symbol key, so a string-keyed
'engine' from the call or the client defaults was sent as well
(engine=google&engine=extract). Drop string-keyed engines and nil out
any inherited from the defaults.

extract_link now goes through extract with symbolized overrides, so it
gets the same engine forcing, string-keyed overrides replace link values
instead of duplicating them, and links without a url fail fast.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants