From 379f946aa88290c4a7e026af00f0e247c5d29a6e Mon Sep 17 00:00:00 2001 From: Josef Strzibny Date: Mon, 17 Aug 2026 10:11:39 +0200 Subject: [PATCH 1/5] Add initial Markdown support --- CHANGELOG.md | 1 + README.md | 26 +++++++++++++- README.md.erb | 26 +++++++++++++- lib/serpapi/client.rb | 36 ++++++++++++++----- lib/serpapi/error.rb | 4 +-- spec/serpapi/client/client_spec.rb | 26 ++++++++++++++ .../serpapi/client/search_archive_api_spec.rb | 12 +++++++ 7 files changed, 119 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7639471..45e5596 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Changelog + * [Unreleased] Add Markdown search and archive output support * [2026-02-23] 1.0.3 Enhance error object #16 * [2025-11-17] 1.0.2 Implement `inspect` functions for client #13 * [2025-07-18] 1.0.1 Add support for old Ruby versions (2.7, 3.0) diff --git a/README.md b/README.md index 1943027..2d83de9 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,34 @@ client.close - [Asynchronous searches](./demo/demo_async.rb) for submitting non-blocking jobs and retrieving completed results from the Search Archive API. - [Persistent connections and connection pooling](./demo/demo_thread_pool.rb) for reusing HTTP connections across searches. -- JSON responses as Ruby hashes with `search`, or raw search-engine HTML with `html`. +- Search results as Ruby hashes with `search`, token-efficient Markdown with `markdown`, or raw search-engine HTML with `html`. - SDK methods for the [Location API](https://serpapi.com/locations-api), [Search Archive API](https://serpapi.com/search-archive-api), and [Account API](https://serpapi.com/account-api). - Configurable HTTP timeouts and symbolized or string JSON keys. +## Response formats + +Use `search` for structured results decoded into a Ruby `Hash`: + +```ruby +results = client.search(q: "coffee") +``` + +Use `markdown` for a token-efficient Markdown `String` optimized for LLMs and AI agents: + +```ruby +markdown = client.markdown(q: "coffee") +``` + +Use `html` when you need the raw search-engine response: + +```ruby +raw_html = client.html(q: "coffee") +``` + +Archived results are also available as Markdown with `client.search_archive(search_id, :markdown)`. + +Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). + ## Configuration Set defaults when creating a client, then override search parameters in individual calls: diff --git a/README.md.erb b/README.md.erb index 200bc5d..773062e 100644 --- a/README.md.erb +++ b/README.md.erb @@ -55,10 +55,34 @@ client.close - [Asynchronous searches](./demo/demo_async.rb) for submitting non-blocking jobs and retrieving completed results from the Search Archive API. - [Persistent connections and connection pooling](./demo/demo_thread_pool.rb) for reusing HTTP connections across searches. -- JSON responses as Ruby hashes with `search`, or raw search-engine HTML with `html`. +- Search results as Ruby hashes with `search`, token-efficient Markdown with `markdown`, or raw search-engine HTML with `html`. - SDK methods for the [Location API](https://serpapi.com/locations-api), [Search Archive API](https://serpapi.com/search-archive-api), and [Account API](https://serpapi.com/account-api). - Configurable HTTP timeouts and symbolized or string JSON keys. +## Response formats + +Use `search` for structured results decoded into a Ruby `Hash`: + +```ruby +results = client.search(q: "coffee") +``` + +Use `markdown` for a token-efficient Markdown `String` optimized for LLMs and AI agents: + +```ruby +markdown = client.markdown(q: "coffee") +``` + +Use `html` when you need the raw search-engine response: + +```ruby +raw_html = client.html(q: "coffee") +``` + +Archived results are also available as Markdown with `client.search_archive(search_id, :markdown)`. + +Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). + ## Configuration Set defaults when creating a client, then override search parameters in individual calls: diff --git a/lib/serpapi/client.rb b/lib/serpapi/client.rb index 0a8f081..f98bf26 100644 --- a/lib/serpapi/client.rb +++ b/lib/serpapi/client.rb @@ -7,7 +7,7 @@ module SerpApi # features: # * async non-block search # * persistent HTTP connection - # * search API + # * search API with JSON, HTML, and Markdown output # * location API # * account API # * search archive API @@ -123,6 +123,15 @@ def html(params = {}) get('/search', :html, params) end + # Perform a search using SerpApi.com and return results optimized for LLMs and AI agents. + # The output contains Markdown tables, links, and YAML frontmatter. + # + # @param [Hash] params includes engine, api_key, search fields and more. + # @return [String] search results formatted as Markdown. + def markdown(params = {}) + get('/search.md', :markdown, params) + end + # Get location using Location API # # example: spec/serpapi/location_api_spec.rb @@ -146,12 +155,13 @@ def location(params = {}) # doc: https://serpapi.com/search-archive-api # # @param [String|Integer] search_id from original search `results[:search_metadata][:id]` - # @param [Symbol] format :json or :html [default: json, optional] - # @return [String|Hash] raw html or JSON / Hash + # @param [Symbol] format :json, :html, or :markdown [default: json, optional] + # @return [String|Hash] raw HTML, Markdown, or JSON / Hash def search_archive(search_id, format = :json) - raise SerpApiError, 'format must be json or html' unless [:json, :html].include?(format) + raise SerpApiError, 'format must be json, html, or markdown' unless [:json, :html, :markdown].include?(format) - get("/searches/#{search_id}.#{format}", format) + extension = format == :markdown ? :md : format + get("/searches/#{search_id}.#{extension}", format) end # Get account information using Account API @@ -211,9 +221,9 @@ def persistent? # Perform HTTP GET request to the SerpApi.com backend endpoint. # # @param [String] endpoint HTTP service URI - # @param [Symbol] decoder type :json or :html + # @param [Symbol] decoder type :json, :html, or :markdown # @param [Hash] params custom search inputs - # @return [String|Hash] raw HTML or decoded response as JSON / Hash + # @return [String|Hash] raw text or decoded response as JSON / Hash def get(endpoint, decoder = :json, params = {}) response = execute_request(endpoint, params) handle_response(response, decoder, endpoint, params) @@ -234,8 +244,10 @@ def handle_response(response, decoder, endpoint, params) process_json_response(response, endpoint, params) when :html process_html_response(response, endpoint, params) + when :markdown + process_markdown_response(response, endpoint, params) else - raise SerpApiError, "not supported decoder: #{decoder}, available: :json, :html" + raise SerpApiError, "not supported decoder: #{decoder}, available: :json, :html, :markdown" end end @@ -258,6 +270,14 @@ def process_html_response(response, endpoint, params) response.body end + def process_markdown_response(response, endpoint, params) + raise_http_error(response, nil, endpoint, params, decoder: :markdown) if response.status != 200 + + data = response.body.to_s + response.flush if persistent? + data + end + def validate_json_content!(data, response, endpoint, params) if data.is_a?(Hash) && data.key?(:error) raise_http_error(response, data, endpoint, params, explicit_error: data[:error]) diff --git a/lib/serpapi/error.rb b/lib/serpapi/error.rb index b082178..625d334 100644 --- a/lib/serpapi/error.rb +++ b/lib/serpapi/error.rb @@ -10,7 +10,7 @@ module SerpApi # - search_params: Hash of search parameters used (optional) # - response_status: Integer HTTP or response status code (optional) # - search_id: String id returned by the service for the search (optional) - # - decoder: Symbol representing the decoder/format used (optional) (e.g. :json) + # - decoder: Symbol representing the decoder/format used (optional) (e.g. :json or :markdown) class SerpApiError < StandardError attr_reader :serpapi_error, :search_params, :response_status, :search_id, :decoder @@ -21,7 +21,7 @@ class SerpApiError < StandardError # @param search_params [Hash, nil] optional hash of the search parameters used # @param response_status [Integer, nil] optional HTTP or response status code # @param search_id [String, nil] optional id returned by the service for the search - # @param decoder [Symbol, nil] optional decoder/format used (e.g. :json) + # @param decoder [Symbol, nil] optional decoder/format used (e.g. :json or :markdown) def initialize(message = nil, serpapi_error: nil, search_params: nil, diff --git a/spec/serpapi/client/client_spec.rb b/spec/serpapi/client/client_spec.rb index d3c2736..6e6fdfe 100644 --- a/spec/serpapi/client/client_spec.rb +++ b/spec/serpapi/client/client_spec.rb @@ -30,6 +30,32 @@ expect(results).to match(/coffee/i) end + it 'search for coffee in Austin, TX and receive Markdown' do + results = client.markdown(q: 'Coffee', location: 'Austin, TX') + + expect(results).to be_a(String) + expect(results).to start_with('---') + expect(results).to include('## Organic Results') + end + + it 'requests the Markdown endpoint' do + response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) + expect(client.socket).to receive(:get) + .with('/search.md', params: hash_including(q: 'Coffee')) + .and_return(response) + + expect(client.markdown(q: 'Coffee')).to start_with('---') + end + + it 'reports Markdown HTTP errors with their decoder' do + response = double(status: 400, body: 'Invalid search') + allow(client.socket).to receive(:get).and_return(response) + + expect { + client.markdown(q: 'Coffee') + }.to raise_error(SerpApi::SerpApiError) { |error| expect(error.decoder).to eq(:markdown) } + end + it 'missing query' do begin client.search diff --git a/spec/serpapi/client/search_archive_api_spec.rb b/spec/serpapi/client/search_archive_api_spec.rb index 0ecc422..60ebe44 100644 --- a/spec/serpapi/client/search_archive_api_spec.rb +++ b/spec/serpapi/client/search_archive_api_spec.rb @@ -32,4 +32,16 @@ expect(archive_search).to eq(results) end end + + it 'fetches an archived search as Markdown' do + client = SerpApi::Client.new(api_key: ENV['SERPAPI_KEY'], engine: 'google') + response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) + + expect(client.socket).to receive(:get) + .with('/searches/search-id.md', params: hash_including(api_key: ENV['SERPAPI_KEY'])) + .and_return(response) + + results = client.search_archive('search-id', :markdown) + expect(results).to eq("---\n## Organic Results\n") + end end From 3fb5120dc5ded1753b5bcdc23a2352a8f4ed71c6 Mon Sep 17 00:00:00 2001 From: Josef Strzibny Date: Mon, 17 Aug 2026 10:47:00 +0200 Subject: [PATCH 2/5] Rename markdown method to md --- README.md | 6 +++--- README.md.erb | 6 +++--- lib/serpapi/client.rb | 2 +- spec/serpapi/client/client_spec.rb | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2d83de9..a083999 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ client.close - [Asynchronous searches](./demo/demo_async.rb) for submitting non-blocking jobs and retrieving completed results from the Search Archive API. - [Persistent connections and connection pooling](./demo/demo_thread_pool.rb) for reusing HTTP connections across searches. -- Search results as Ruby hashes with `search`, token-efficient Markdown with `markdown`, or raw search-engine HTML with `html`. +- Search results as Ruby hashes with `search`, token-efficient Markdown with `md`, or raw search-engine HTML with `html`. - SDK methods for the [Location API](https://serpapi.com/locations-api), [Search Archive API](https://serpapi.com/search-archive-api), and [Account API](https://serpapi.com/account-api). - Configurable HTTP timeouts and symbolized or string JSON keys. @@ -67,10 +67,10 @@ Use `search` for structured results decoded into a Ruby `Hash`: results = client.search(q: "coffee") ``` -Use `markdown` for a token-efficient Markdown `String` optimized for LLMs and AI agents: +Use `md` for a token-efficient Markdown `String` optimized for LLMs and AI agents: ```ruby -markdown = client.markdown(q: "coffee") +markdown = client.md(q: "coffee") ``` Use `html` when you need the raw search-engine response: diff --git a/README.md.erb b/README.md.erb index 773062e..a0d1a95 100644 --- a/README.md.erb +++ b/README.md.erb @@ -55,7 +55,7 @@ client.close - [Asynchronous searches](./demo/demo_async.rb) for submitting non-blocking jobs and retrieving completed results from the Search Archive API. - [Persistent connections and connection pooling](./demo/demo_thread_pool.rb) for reusing HTTP connections across searches. -- Search results as Ruby hashes with `search`, token-efficient Markdown with `markdown`, or raw search-engine HTML with `html`. +- Search results as Ruby hashes with `search`, token-efficient Markdown with `md`, or raw search-engine HTML with `html`. - SDK methods for the [Location API](https://serpapi.com/locations-api), [Search Archive API](https://serpapi.com/search-archive-api), and [Account API](https://serpapi.com/account-api). - Configurable HTTP timeouts and symbolized or string JSON keys. @@ -67,10 +67,10 @@ Use `search` for structured results decoded into a Ruby `Hash`: results = client.search(q: "coffee") ``` -Use `markdown` for a token-efficient Markdown `String` optimized for LLMs and AI agents: +Use `md` for a token-efficient Markdown `String` optimized for LLMs and AI agents: ```ruby -markdown = client.markdown(q: "coffee") +markdown = client.md(q: "coffee") ``` Use `html` when you need the raw search-engine response: diff --git a/lib/serpapi/client.rb b/lib/serpapi/client.rb index f98bf26..63d9971 100644 --- a/lib/serpapi/client.rb +++ b/lib/serpapi/client.rb @@ -128,7 +128,7 @@ def html(params = {}) # # @param [Hash] params includes engine, api_key, search fields and more. # @return [String] search results formatted as Markdown. - def markdown(params = {}) + def md(params = {}) get('/search.md', :markdown, params) end diff --git a/spec/serpapi/client/client_spec.rb b/spec/serpapi/client/client_spec.rb index 6e6fdfe..28b2822 100644 --- a/spec/serpapi/client/client_spec.rb +++ b/spec/serpapi/client/client_spec.rb @@ -31,7 +31,7 @@ end it 'search for coffee in Austin, TX and receive Markdown' do - results = client.markdown(q: 'Coffee', location: 'Austin, TX') + results = client.md(q: 'Coffee', location: 'Austin, TX') expect(results).to be_a(String) expect(results).to start_with('---') @@ -44,7 +44,7 @@ .with('/search.md', params: hash_including(q: 'Coffee')) .and_return(response) - expect(client.markdown(q: 'Coffee')).to start_with('---') + expect(client.md(q: 'Coffee')).to start_with('---') end it 'reports Markdown HTTP errors with their decoder' do @@ -52,7 +52,7 @@ allow(client.socket).to receive(:get).and_return(response) expect { - client.markdown(q: 'Coffee') + client.md(q: 'Coffee') }.to raise_error(SerpApi::SerpApiError) { |error| expect(error.decoder).to eq(:markdown) } end From b063cf80f960e90cb37ab061e0d12bebde3aa274 Mon Sep 17 00:00:00 2001 From: Josef Strzibny Date: Mon, 17 Aug 2026 11:45:48 +0200 Subject: [PATCH 3/5] Update archive to :md --- README.md | 2 +- README.md.erb | 2 +- lib/serpapi/client.rb | 22 ++++++++++--------- lib/serpapi/error.rb | 4 ++-- spec/serpapi/client/client_spec.rb | 2 +- .../serpapi/client/search_archive_api_spec.rb | 16 ++++++++++++-- 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a083999..df1c784 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Use `html` when you need the raw search-engine response: raw_html = client.html(q: "coffee") ``` -Archived results are also available as Markdown with `client.search_archive(search_id, :markdown)`. +Archived results are also available as Markdown with `client.search_archive(search_id, :md)` or `client.search_archive(search_id, output: "md")`. Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). diff --git a/README.md.erb b/README.md.erb index a0d1a95..a3c6864 100644 --- a/README.md.erb +++ b/README.md.erb @@ -79,7 +79,7 @@ Use `html` when you need the raw search-engine response: raw_html = client.html(q: "coffee") ``` -Archived results are also available as Markdown with `client.search_archive(search_id, :markdown)`. +Archived results are also available as Markdown with `client.search_archive(search_id, :md)` or `client.search_archive(search_id, output: "md")`. Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). diff --git a/lib/serpapi/client.rb b/lib/serpapi/client.rb index 63d9971..49fc571 100644 --- a/lib/serpapi/client.rb +++ b/lib/serpapi/client.rb @@ -129,7 +129,7 @@ def html(params = {}) # @param [Hash] params includes engine, api_key, search fields and more. # @return [String] search results formatted as Markdown. def md(params = {}) - get('/search.md', :markdown, params) + get('/search.md', :md, params) end # Get location using Location API @@ -155,13 +155,15 @@ def location(params = {}) # doc: https://serpapi.com/search-archive-api # # @param [String|Integer] search_id from original search `results[:search_metadata][:id]` - # @param [Symbol] format :json, :html, or :markdown [default: json, optional] + # @param [Symbol] format :json, :html, or :md [default: json, optional] + # @param [String|Symbol, nil] output response format using the SerpApi output parameter [optional] # @return [String|Hash] raw HTML, Markdown, or JSON / Hash - def search_archive(search_id, format = :json) - raise SerpApiError, 'format must be json, html, or markdown' unless [:json, :html, :markdown].include?(format) + def search_archive(search_id, format = :json, output: nil) + format = output.to_s.to_sym unless output.nil? + raise SerpApiError, 'format must be json, html, or md' unless [:json, :html, :md].include?(format) - extension = format == :markdown ? :md : format - get("/searches/#{search_id}.#{extension}", format) + endpoint = output.nil? ? "/searches/#{search_id}.#{format}" : "/searches/#{search_id}" + get(endpoint, format, output.nil? ? {} : { output: output }) end # Get account information using Account API @@ -221,7 +223,7 @@ def persistent? # Perform HTTP GET request to the SerpApi.com backend endpoint. # # @param [String] endpoint HTTP service URI - # @param [Symbol] decoder type :json, :html, or :markdown + # @param [Symbol] decoder type :json, :html, or :md # @param [Hash] params custom search inputs # @return [String|Hash] raw text or decoded response as JSON / Hash def get(endpoint, decoder = :json, params = {}) @@ -244,10 +246,10 @@ def handle_response(response, decoder, endpoint, params) process_json_response(response, endpoint, params) when :html process_html_response(response, endpoint, params) - when :markdown + when :md process_markdown_response(response, endpoint, params) else - raise SerpApiError, "not supported decoder: #{decoder}, available: :json, :html, :markdown" + raise SerpApiError, "not supported decoder: #{decoder}, available: :json, :html, :md" end end @@ -271,7 +273,7 @@ def process_html_response(response, endpoint, params) end def process_markdown_response(response, endpoint, params) - raise_http_error(response, nil, endpoint, params, decoder: :markdown) if response.status != 200 + raise_http_error(response, nil, endpoint, params, decoder: :md) if response.status != 200 data = response.body.to_s response.flush if persistent? diff --git a/lib/serpapi/error.rb b/lib/serpapi/error.rb index 625d334..f2c3fea 100644 --- a/lib/serpapi/error.rb +++ b/lib/serpapi/error.rb @@ -10,7 +10,7 @@ module SerpApi # - search_params: Hash of search parameters used (optional) # - response_status: Integer HTTP or response status code (optional) # - search_id: String id returned by the service for the search (optional) - # - decoder: Symbol representing the decoder/format used (optional) (e.g. :json or :markdown) + # - decoder: Symbol representing the decoder/format used (optional) (e.g. :json or :md) class SerpApiError < StandardError attr_reader :serpapi_error, :search_params, :response_status, :search_id, :decoder @@ -21,7 +21,7 @@ class SerpApiError < StandardError # @param search_params [Hash, nil] optional hash of the search parameters used # @param response_status [Integer, nil] optional HTTP or response status code # @param search_id [String, nil] optional id returned by the service for the search - # @param decoder [Symbol, nil] optional decoder/format used (e.g. :json or :markdown) + # @param decoder [Symbol, nil] optional decoder/format used (e.g. :json or :md) def initialize(message = nil, serpapi_error: nil, search_params: nil, diff --git a/spec/serpapi/client/client_spec.rb b/spec/serpapi/client/client_spec.rb index 28b2822..3e9a94d 100644 --- a/spec/serpapi/client/client_spec.rb +++ b/spec/serpapi/client/client_spec.rb @@ -53,7 +53,7 @@ expect { client.md(q: 'Coffee') - }.to raise_error(SerpApi::SerpApiError) { |error| expect(error.decoder).to eq(:markdown) } + }.to raise_error(SerpApi::SerpApiError) { |error| expect(error.decoder).to eq(:md) } end it 'missing query' do diff --git a/spec/serpapi/client/search_archive_api_spec.rb b/spec/serpapi/client/search_archive_api_spec.rb index 60ebe44..d9d817c 100644 --- a/spec/serpapi/client/search_archive_api_spec.rb +++ b/spec/serpapi/client/search_archive_api_spec.rb @@ -33,7 +33,7 @@ end end - it 'fetches an archived search as Markdown' do + it 'fetches an archived search as Markdown using the md format' do client = SerpApi::Client.new(api_key: ENV['SERPAPI_KEY'], engine: 'google') response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) @@ -41,7 +41,19 @@ .with('/searches/search-id.md', params: hash_including(api_key: ENV['SERPAPI_KEY'])) .and_return(response) - results = client.search_archive('search-id', :markdown) + results = client.search_archive('search-id', :md) + expect(results).to eq("---\n## Organic Results\n") + end + + it 'fetches an archived search as Markdown using the output parameter' do + client = SerpApi::Client.new(api_key: ENV['SERPAPI_KEY'], engine: 'google') + response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) + + expect(client.socket).to receive(:get) + .with('/searches/search-id', params: hash_including(output: 'md')) + .and_return(response) + + results = client.search_archive('search-id', output: 'md') expect(results).to eq("---\n## Organic Results\n") end end From 21b5347f3c8754839972d91d94980154440df789 Mon Sep 17 00:00:00 2001 From: Josef Strzibny Date: Tue, 18 Aug 2026 10:50:09 +0200 Subject: [PATCH 4/5] Remove double and keep only live tests --- spec/serpapi/client/client_spec.rb | 14 +-------- .../serpapi/client/search_archive_api_spec.rb | 31 +++++-------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/spec/serpapi/client/client_spec.rb b/spec/serpapi/client/client_spec.rb index 3e9a94d..082ce4a 100644 --- a/spec/serpapi/client/client_spec.rb +++ b/spec/serpapi/client/client_spec.rb @@ -38,21 +38,9 @@ expect(results).to include('## Organic Results') end - it 'requests the Markdown endpoint' do - response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) - expect(client.socket).to receive(:get) - .with('/search.md', params: hash_including(q: 'Coffee')) - .and_return(response) - - expect(client.md(q: 'Coffee')).to start_with('---') - end - it 'reports Markdown HTTP errors with their decoder' do - response = double(status: 400, body: 'Invalid search') - allow(client.socket).to receive(:get).and_return(response) - expect { - client.md(q: 'Coffee') + client.md }.to raise_error(SerpApi::SerpApiError) { |error| expect(error.decoder).to eq(:md) } end diff --git a/spec/serpapi/client/search_archive_api_spec.rb b/spec/serpapi/client/search_archive_api_spec.rb index d9d817c..acfc5db 100644 --- a/spec/serpapi/client/search_archive_api_spec.rb +++ b/spec/serpapi/client/search_archive_api_spec.rb @@ -26,34 +26,17 @@ client = SerpApi::Client.new(api_key: client.api_key, engine: 'google') results = client.search_archive(search_id) expect(archive_search).to eq(results) + + markdown = client.search_archive(search_id, :md) + expect(markdown).to be_a(String) + expect(markdown).to start_with('---') + + markdown_from_output = client.search_archive(search_id, output: 'md') + expect(markdown_from_output).to eq(markdown) else client = SerpApi::Client.new(api_key: client.api_key, engine: 'google') allow(client).to receive(:get) { search_response_mock } expect(archive_search).to eq(results) end end - - it 'fetches an archived search as Markdown using the md format' do - client = SerpApi::Client.new(api_key: ENV['SERPAPI_KEY'], engine: 'google') - response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) - - expect(client.socket).to receive(:get) - .with('/searches/search-id.md', params: hash_including(api_key: ENV['SERPAPI_KEY'])) - .and_return(response) - - results = client.search_archive('search-id', :md) - expect(results).to eq("---\n## Organic Results\n") - end - - it 'fetches an archived search as Markdown using the output parameter' do - client = SerpApi::Client.new(api_key: ENV['SERPAPI_KEY'], engine: 'google') - response = double(status: 200, body: "---\n## Organic Results\n", flush: :clean) - - expect(client.socket).to receive(:get) - .with('/searches/search-id', params: hash_including(output: 'md')) - .and_return(response) - - results = client.search_archive('search-id', output: 'md') - expect(results).to eq("---\n## Organic Results\n") - end end From d006bcb2ee229f2df76e605473092305f8237108 Mon Sep 17 00:00:00 2001 From: Josef Strzibny Date: Tue, 18 Aug 2026 12:32:17 +0200 Subject: [PATCH 5/5] Pass .md directly in search_archive --- README.md | 2 +- README.md.erb | 2 +- lib/serpapi/client.rb | 7 ++----- spec/serpapi/client/search_archive_api_spec.rb | 3 --- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index df1c784..fc221af 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Use `html` when you need the raw search-engine response: raw_html = client.html(q: "coffee") ``` -Archived results are also available as Markdown with `client.search_archive(search_id, :md)` or `client.search_archive(search_id, output: "md")`. +Archived results are also available as Markdown with `client.search_archive(search_id, :md)`. Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). diff --git a/README.md.erb b/README.md.erb index a3c6864..29a9f83 100644 --- a/README.md.erb +++ b/README.md.erb @@ -79,7 +79,7 @@ Use `html` when you need the raw search-engine response: raw_html = client.html(q: "coffee") ``` -Archived results are also available as Markdown with `client.search_archive(search_id, :md)` or `client.search_archive(search_id, output: "md")`. +Archived results are also available as Markdown with `client.search_archive(search_id, :md)`. Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). diff --git a/lib/serpapi/client.rb b/lib/serpapi/client.rb index 49fc571..4725ca9 100644 --- a/lib/serpapi/client.rb +++ b/lib/serpapi/client.rb @@ -156,14 +156,11 @@ def location(params = {}) # # @param [String|Integer] search_id from original search `results[:search_metadata][:id]` # @param [Symbol] format :json, :html, or :md [default: json, optional] - # @param [String|Symbol, nil] output response format using the SerpApi output parameter [optional] # @return [String|Hash] raw HTML, Markdown, or JSON / Hash - def search_archive(search_id, format = :json, output: nil) - format = output.to_s.to_sym unless output.nil? + def search_archive(search_id, format = :json) raise SerpApiError, 'format must be json, html, or md' unless [:json, :html, :md].include?(format) - endpoint = output.nil? ? "/searches/#{search_id}.#{format}" : "/searches/#{search_id}" - get(endpoint, format, output.nil? ? {} : { output: output }) + get("/searches/#{search_id}.#{format}", format) end # Get account information using Account API diff --git a/spec/serpapi/client/search_archive_api_spec.rb b/spec/serpapi/client/search_archive_api_spec.rb index acfc5db..2586653 100644 --- a/spec/serpapi/client/search_archive_api_spec.rb +++ b/spec/serpapi/client/search_archive_api_spec.rb @@ -30,9 +30,6 @@ markdown = client.search_archive(search_id, :md) expect(markdown).to be_a(String) expect(markdown).to start_with('---') - - markdown_from_output = client.search_archive(search_id, output: 'md') - expect(markdown_from_output).to eq(markdown) else client = SerpApi::Client.new(api_key: client.api_key, engine: 'google') allow(client).to receive(:get) { search_response_mock }