Query Google from PHP and get structured results, using serp.cheap. One endpoint: POST https://api.serp.cheap/v1/search with an X-API-Key header. Sanity-check your key from the shell first:
curl -s https://api.serp.cheap/v1/search \
-H "X-API-Key: $SERP_CHEAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"best electric cars 2026","gl":"us"}'
All PHP examples below read the key from the SERP_CHEAP_API_KEY environment variable — keep it out of source control and client output.
Native cURL (no dependencies)
<?php
$apiKey = getenv("SERP_CHEAP_API_KEY");
if ($apiKey === false) {
fwrite(STDERR, "SERP_CHEAP_API_KEY is not set\n");
exit(1);
}
$ch = curl_init("https://api.serp.cheap/v1/search");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["q" => "best electric cars 2026", "gl" => "us", "page" => 1]),
]);
$res = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($res === false || $code !== 200) {
throw new RuntimeException("Search failed: HTTP $code");
}
$data = json_decode($res, true);
foreach ($data["organic"] as $r) {
printf("#%d %s — %s\n", $r["position"], $r["title"], $r["link"]);
}
Output:
#1 Best Electric Cars of 2026, Ranked — https://www.example.com/ev/best-electric-cars-2026
#2 Top 10 EVs Worth Buying This Year — https://www.example.org/reviews/top-evs
#3 Electric car buyer's guide (2026) — https://blog.example.net/ev-buyers-guide
...
Each entry also carries snippet, date, and sitelinks when Google shows them, and the response includes a knowledgeGraph object when a Knowledge Graph panel is present.
With Guzzle
<?php
require "vendor/autoload.php";
use GuzzleHttp\Client;
$client = new Client(["base_uri" => "https://api.serp.cheap/"]);
$res = $client->post("v1/search", [
"headers" => ["X-API-Key" => getenv("SERP_CHEAP_API_KEY")],
"json" => ["q" => "serp api php", "gl" => "us"],
]);
$data = json_decode((string) $res->getBody(), true);
echo "cost: {$data['stats']['cost']} credits\n";
Guzzle throws on non-2xx responses by default, so failed requests surface as exceptions without extra checks.
Pagination
page is 1-indexed; each page is a separate billed request. Wrap the cURL call from above in a function and loop:
<?php
function serpSearch(array $params): array {
$apiKey = getenv("SERP_CHEAP_API_KEY");
if ($apiKey === false) {
throw new RuntimeException("SERP_CHEAP_API_KEY is not set");
}
$ch = curl_init("https://api.serp.cheap/v1/search");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["X-API-Key: $apiKey", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode($params),
]);
$res = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($res === false || $code !== 200) {
throw new RuntimeException("Search failed: HTTP $code");
}
return json_decode($res, true);
}
$all = [];
for ($page = 1; $page <= 3; $page++) {
$data = serpSearch(["q" => "best electric cars 2026", "gl" => "us", "page" => $page]);
if (empty($data["organic"])) {
break; // no more results
}
$all = array_merge($all, $data["organic"]);
}
printf("collected %d results\n", count($all));
Stop as soon as a page comes back empty — every extra page costs credits.
Next
- How to use the Google Search API (full reference)
- Other languages: Python · Node.js · Go
Try serp.cheap
Drop either snippet into your PHP project and you’re querying Google in a few minutes. 100 free searches on signup — no card required; pay-as-you-go from $0.75/1k after that, and credits stay valid for 12 months.
- Get your free API key →
- Prefer paying by card? serp.cheap is also on RapidAPI (
$0.90/1k) and Apify ($1.50/1k).