Documentation Index
Fetch the complete documentation index at: https://docs.geekflare.com/llms.txt
Use this file to discover all available pages before exploring further.
The Geekflare SDK must only run server-side — your API key must never be exposed to the browser. Next.js makes this straightforward with Route Handlers, Server Actions, and Server Components.
Prerequisites
- Next.js 14+ with the App Router
- Node.js 18+
- A Geekflare API key — get one free
Installation
bash pnpm add @geekflare/api-node
bash npm install @geekflare/api-node
bash yarn add @geekflare/api-node
Set your API key
Add your API key to .env.local:
GEEKFLARE_API_KEY=your_api_key_here
.env.local is gitignored by default in Next.js. Never prefix this variable
with NEXT_PUBLIC_ — that would expose it to the browser.
Shared client
Create a single client instance to reuse across your app:
import { GeekflareClient } from "@geekflare/api-node";
export const geekflare = new GeekflareClient({
apiKey: process.env.GEEKFLARE_API_KEY!,
});
Web Scraping
Route Handler
Accepts a POST request from your client-side code and returns scraped content.
import { NextResponse } from "next/server";
import { geekflare } from "@/lib/geekflare";
export async function POST(request: Request) {
const { url } = await request.json();
if (!url) {
return NextResponse.json({ error: "url is required" }, { status: 400 });
}
try {
const result = await geekflare.webScrape({
url,
renderJS: true,
blockAds: true,
format: "html,markdown",
});
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json(
{ error: error.message ?? "Scrape failed" },
{ status: error?.status ?? 500 },
);
}
}
Server Action
Use directly from Client Components without a round-trip to a route:
"use server";
import { geekflare } from "@/lib/geekflare";
export async function scrapeUrl(url: string) {
const result = await geekflare.webScrape({
url,
renderJS: true,
blockAds: true,
format: "markdown",
});
return result;
}
Server Component
Fetch and render scraped content directly in a Server Component:
import { geekflare } from "@/lib/geekflare";
export default async function ScrapePage() {
const result = await geekflare.webScrape({
url: "https://toscrape.com/",
format: "markdown",
});
// result.data is a URL pointing to the scraped output file
return (
<article className="prose mx-auto py-12">
<h1>Scraped Content</h1>
<p>
Output file: <a href={result.data as string}>{result.data as string}</a>
</p>
</article>
);
}
Search
Route Handler
import { NextResponse } from "next/server";
import { geekflare } from "@/lib/geekflare";
export async function POST(request: Request) {
const { query } = await request.json();
if (!query) {
return NextResponse.json({ error: "query is required" }, { status: 400 });
}
try {
const result = await geekflare.search({
query,
limit: 10,
location: "us",
source: "web",
format: "json",
});
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json(
{ error: error.message ?? "Search failed" },
{ status: error?.status ?? 500 },
);
}
}
Server Action
"use server";
import { geekflare } from "@/lib/geekflare";
export async function searchWeb(query: string) {
const result = await geekflare.search({
query,
limit: 10,
location: "us",
source: "web",
format: "json",
});
return result.data ?? [];
}
Server Component
import { geekflare } from "@/lib/geekflare";
export default async function SearchPage({
searchParams,
}: {
searchParams: { q?: string };
}) {
const query = searchParams.q ?? "Next.js best practices";
const result = await geekflare.search({
query,
limit: 10,
location: "us",
format: "json",
});
return (
<div className="max-w-2xl mx-auto py-12">
<h1 className="text-2xl font-bold mb-6">Results for: {query}</h1>
<ul className="space-y-4">
{result.data?.map((item: any) => (
<li key={item.url}>
<a
href={item.url}
className="text-blue-600 hover:underline font-medium"
>
{item.title}
</a>
<p className="text-sm text-gray-600 mt-1">{item.snippet}</p>
</li>
))}
</ul>
</div>
);
}
Screenshot
Route Handler
app/api/screenshot/route.ts
import { NextResponse } from "next/server";
import { geekflare } from "@/lib/geekflare";
export async function POST(request: Request) {
const { url } = await request.json();
if (!url) {
return NextResponse.json({ error: "url is required" }, { status: 400 });
}
try {
const result = await geekflare.screenshot({
url,
type: "png",
fullPage: true,
device: "desktop",
viewportWidth: 1280,
viewportHeight: 800,
blockAds: true,
hideCookie: true,
});
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json(
{ error: error.message ?? "Screenshot failed" },
{ status: error?.status ?? 500 },
);
}
}
Server Component
Render the screenshot directly in a page using next/image:
import Image from "next/image";
import { geekflare } from "@/lib/geekflare";
export default async function PreviewPage() {
const result = await geekflare.screenshot({
url: "https://example.com",
type: "png",
fullPage: false,
viewportWidth: 1280,
viewportHeight: 800,
blockAds: true,
hideCookie: true,
});
// result.data is the screenshot URL directly
const screenshotUrl = result.data as string;
return (
<div className="p-8">
<h1 className="text-xl font-semibold mb-4">Site Preview</h1>
{screenshotUrl && (
<Image
src={screenshotUrl}
alt="Site screenshot"
width={1280}
height={800}
className="rounded-lg border shadow"
/>
)}
</div>
);
}
Error handling
Handle errors consistently across Route Handlers and Server Actions.
Route Handler pattern
try {
const result = await geekflare.webScrape({ url });
return NextResponse.json(result);
} catch (error: any) {
const code = error?.apiCode ?? error?.status ?? 500;
const message = error?.message ?? "An unexpected error occurred";
if (code === 429) {
return NextResponse.json(
{ error: "Rate limit exceeded. Please try again shortly." },
{ status: 429 },
);
}
if (code === 402) {
return NextResponse.json(
{ error: "API credits exhausted." },
{ status: 402 },
);
}
return NextResponse.json({ error: message }, { status: code });
}
Server Action pattern
"use server";
export async function safeScrape(url: string) {
try {
const result = await geekflare.webScrape({ url, format: "markdown" });
return { data: result.data, error: null };
} catch (error: any) {
return { data: null, error: error.message ?? "Failed to scrape" };
}
}
Consume it safely in a Client Component:
const { data, error } = await safeScrape(url);
if (error) {
return <p className="text-red-500">{error}</p>;
}
Next steps