From serpapi-pack
Production-ready SerpApi client patterns with caching, typing, and multi-engine support. Use when building search services, implementing result caching, or wrapping SerpApi with typed responses. Trigger: "serpapi patterns", "serpapi best practices", "serpapi client wrapper".
npx claudepluginhub flight505/skill-forge --plugin serpapi-packThis skill is limited to using the following tools:
Production patterns for SerpApi: typed result interfaces, response caching (critical since each search costs credits), multi-engine abstraction, and async search with the Searches Archive API.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Production patterns for SerpApi: typed result interfaces, response caching (critical since each search costs credits), multi-engine abstraction, and async search with the Searches Archive API.
interface SerpApiOrganicResult {
position: number;
title: string;
link: string;
snippet: string;
displayed_link: string;
source?: string;
}
interface SerpApiSearchResult {
search_metadata: { id: string; status: string; created_at: string };
search_parameters: Record<string, string>;
organic_results: SerpApiOrganicResult[];
answer_box?: { answer?: string; snippet?: string; title?: string };
knowledge_graph?: { title: string; description?: string; type?: string };
related_questions?: Array<{ question: string; snippet: string }>;
pagination?: { next: string };
}
import { getJson } from 'serpapi';
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, SerpApiSearchResult>({
max: 500,
ttl: 3600_000, // 1 hour -- search results are relatively stable
});
async function cachedSearch(params: Record<string, any>): Promise<SerpApiSearchResult> {
const key = JSON.stringify(params);
const cached = cache.get(key);
if (cached) return cached;
const result = await getJson({
...params,
api_key: process.env.SERPAPI_API_KEY,
}) as SerpApiSearchResult;
cache.set(key, result);
return result;
}
import serpapi, os
class SearchService:
ENGINES = {
"web": {"engine": "google", "query_param": "q"},
"news": {"engine": "google_news", "query_param": "q"},
"images": {"engine": "google_images", "query_param": "q"},
"youtube": {"engine": "youtube", "query_param": "search_query"},
"bing": {"engine": "bing", "query_param": "q"},
"shopping": {"engine": "google_shopping", "query_param": "q"},
}
def __init__(self):
self.client = serpapi.Client(api_key=os.environ["SERPAPI_API_KEY"])
def search(self, query: str, engine: str = "web", **kwargs) -> dict:
config = self.ENGINES[engine]
params = {
"engine": config["engine"],
config["query_param"]: query,
**kwargs,
}
return self.client.search(**params)
# Usage
svc = SearchService()
web = svc.search("Claude AI")
news = svc.search("Claude AI", engine="news")
videos = svc.search("Claude AI tutorial", engine="youtube")
# Submit search asynchronously -- retrieve later
result = client.search(engine="google", q="expensive query", async_search=True)
search_id = result["search_metadata"]["id"]
# Later: retrieve from archive (no extra credit charge)
import time
while True:
archived = client.search(engine="google", search_id=search_id)
if archived["search_metadata"]["status"] == "Success":
break
time.sleep(2)
| Pattern | Use Case | Benefit |
|---|---|---|
| LRU cache | Repeated queries | Saves API credits |
| Engine abstraction | Multi-engine | Clean API for consumers |
| Async search | Heavy queries | Non-blocking, same credit cost |
| Type interfaces | All usage | Catch response changes early |
Apply patterns in serpapi-core-workflow-a for real-world usage.