diff --git a/README.md b/README.md index 41abac3..586dcb0 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,30 @@ This example runs a search for "coffee" on Google. It returns the results as a P See the [playground](https://serpapi.com/playground) to generate your own code. +## Response formats + +Use `search` for structured results decoded into a PHP object: + +```php +$results = $client->search(['q' => 'coffee']); +``` + +Use `md` for a token-efficient Markdown string optimized for LLMs and AI agents: + +```php +$markdown = $client->md(['q' => 'coffee']); +``` + +Use `html` when you need the raw search-engine response: + +```php +$html = $client->html(['q' => 'coffee']); +``` + +Archived results are also available as Markdown with `$client->searchArchive($searchId, 'md')`. + +Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). + ## Configuration ### API key @@ -468,6 +492,9 @@ Now retrieve the previous search from the archive (free of charge): ```php $archived = $client->searchArchive($search_id); print_r($archived); + +$markdown = $client->searchArchive($search_id, 'md'); +echo $markdown; ``` ### Account API diff --git a/README.md.erb b/README.md.erb index 389c0b0..54e3ba5 100644 --- a/README.md.erb +++ b/README.md.erb @@ -86,6 +86,30 @@ This example runs a search for "coffee" on Google. It returns the results as a P See the [playground](https://serpapi.com/playground) to generate your own code. +## Response formats + +Use `search` for structured results decoded into a PHP object: + +```php +$results = $client->search(['q' => 'coffee']); +``` + +Use `md` for a token-efficient Markdown string optimized for LLMs and AI agents: + +```php +$markdown = $client->md(['q' => 'coffee']); +``` + +Use `html` when you need the raw search-engine response: + +```php +$html = $client->html(['q' => 'coffee']); +``` + +Archived results are also available as Markdown with `$client->searchArchive($searchId, 'md')`. + +Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output). + ## Configuration ### API key @@ -244,6 +268,9 @@ Now retrieve the previous search from the archive (free of charge): ```php $archived = $client->searchArchive($search_id); print_r($archived); + +$markdown = $client->searchArchive($search_id, 'md'); +echo $markdown; ``` ### Account API diff --git a/src/Client.php b/src/Client.php index 2d4b034..156b83c 100644 --- a/src/Client.php +++ b/src/Client.php @@ -76,6 +76,17 @@ public function search(array $params = []): object return $this->get('/search', 'json', $params); } + /** + * Run a search and return Markdown optimized for LLMs and AI agents. + * + * @param array $params + * @throws SerpApiException + */ + public function md(array $params = []): string + { + return $this->get('/search.md', 'md', $params); + } + /** * Run a search and return raw HTML. * @@ -122,8 +133,8 @@ public function searchArchive(string $searchId, string $format = 'json') throw new SerpApiException('search_id must be present'); } - if (!in_array($format, ['json', 'html'], true)) { - throw new SerpApiException('format must be json or html'); + if (!in_array($format, ['json', 'html', 'md'], true)) { + throw new SerpApiException('format must be json, html, or md'); } $safeSearchId = rawurlencode($searchId); @@ -137,8 +148,8 @@ public function searchArchive(string $searchId, string $format = 'json') */ private function get(string $endpoint, string $format = 'json', array $params = []) { - if (!in_array($format, ['json', 'html'], true)) { - throw new SerpApiException("Unsupported format '$format'. Expected 'html' or 'json'."); + if (!in_array($format, ['json', 'html', 'md'], true)) { + throw new SerpApiException("Unsupported format '$format'. Expected 'html', 'json', or 'md'."); } $apiKey = $params['api_key'] ?? $this->apiKey; @@ -171,12 +182,12 @@ private function get(string $endpoint, string $format = 'json', array $params = throw new SerpApiException('cURL error: ' . $curlError); } - if ($format === 'html') { + if (in_array($format, ['html', 'md'], true)) { if ($httpCode === 200) { return $response; } - $this->raiseHttpError($httpCode, $endpoint, $query, null, null, 'html'); + $this->raiseHttpError($httpCode, $endpoint, $query, null, null, $format); } $decoded = json_decode($response); diff --git a/tests/ClientIntegrationTest.php b/tests/ClientIntegrationTest.php index 2acd6db..6c73272 100644 --- a/tests/ClientIntegrationTest.php +++ b/tests/ClientIntegrationTest.php @@ -30,6 +30,14 @@ public function testHtml() $this->assertGreaterThan(10000, strlen($response)); } + public function testMd() + { + $client = $this->serpApiClient(); + $response = $client->md($this->searchParams); + $this->assertStringStartsWith('---', $response); + $this->assertStringContainsString('## Organic Results', $response); + } + public function testSearch() { $client = $this->serpApiClient(); @@ -54,5 +62,9 @@ public function testSearchArchive() $result = $client->search($this->searchParams); $archived_result = $client->searchArchive($result->search_metadata->id); $this->assertEquals($result->search_metadata->id, $archived_result->search_metadata->id); + + $archived_markdown = $client->searchArchive($result->search_metadata->id, 'md'); + $this->assertStringStartsWith('---', $archived_markdown); + $this->assertStringContainsString('## Organic Results', $archived_markdown); } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index c2f3920..4cebd51 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -46,7 +46,7 @@ public function testSearchArchiveThrowsWhenIdEmpty() public function testSearchArchiveThrowsWhenFormatInvalid() { $this->expectException(SerpApiException::class); - $this->expectExceptionMessage('format must be json or html'); + $this->expectExceptionMessage('format must be json, html, or md'); $client = new Client('test_key'); $client->searchArchive('abc', 'xml'); }