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.
Cloudflare Workers run on the V8 edge runtime — no Node.js, no npm packages needed. All three examples use the native fetch API and store the API key as a Wrangler secret so it never touches your codebase.
Prerequisites
Create a Worker
pnpm create cloudflare@latest geekflare-worker
cd geekflare-worker
Choose “Hello World” Worker when prompted. Select TypeScript.
Set your API key
Store the key as a secret — never hardcode it:
# Local development
echo "GEEKFLARE_API_KEY=your_api_key_here" >> .dev.vars
# Production
wrangler secret put GEEKFLARE_API_KEY
.dev.vars is the Workers equivalent of .env. It is gitignored by default.
Do not commit it.
Declare the binding in wrangler.toml so TypeScript knows it exists:
name = "geekflare-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[vars]
# Non-secret vars go here. Secrets are set via `wrangler secret put`.
Add the type to your Worker’s Env interface:
export interface Env {
GEEKFLARE_API_KEY: string;
}
Shared helper
A small typed helper keeps each handler clean:
export const GEEKFLARE_BASE = "https://api.geekflare.com";
export async function geekflarePost<T>(
env: { GEEKFLARE_API_KEY: string },
path: string,
body: Record<string, unknown>,
): Promise<T> {
const res = await fetch(`${GEEKFLARE_BASE}${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": env.GEEKFLARE_API_KEY,
},
body: JSON.stringify(body),
});
if (!res.ok) {
const error = await res.json<{ apiCode: number; message: string }>();
throw new Error(`[${error.apiCode}] ${error.message}`);
}
return res.json<T>();
}
Web Scraping
import { geekflarePost } from "./geekflare";
export interface Env {
GEEKFLARE_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const { url } = await request.json<{ url: string }>();
if (!url) {
return Response.json({ error: "url is required" }, { status: 400 });
}
try {
const result = await geekflarePost(env, "/webscraping", {
url,
renderJS: true,
blockAds: true,
format: "html,markdown",
});
return Response.json(result);
} catch (error: any) {
return Response.json({ error: error.message }, { status: 500 });
}
},
};
Test locally:
wrangler dev
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{ "url": "https://toscrape.com/" }'
Search
Route requests to different Geekflare endpoints based on the URL path:
import { geekflarePost } from "./geekflare";
export interface Env {
GEEKFLARE_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { pathname } = new URL(request.url);
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
if (pathname === "/search") {
const {
query,
limit = 10,
location = "us",
} = await request.json<{
query: string;
limit?: number;
location?: string;
}>();
if (!query) {
return Response.json({ error: "query is required" }, { status: 400 });
}
try {
const result = await geekflarePost(env, "/search", {
query,
limit,
location,
source: "web",
format: "json",
});
return Response.json(result);
} catch (error: any) {
return Response.json({ error: error.message }, { status: 500 });
}
}
return new Response("Not Found", { status: 404 });
},
};
Test locally:
curl -X POST http://localhost:8787/search \
-H "Content-Type: application/json" \
-d '{ "query": "Cloudflare Workers best practices", "limit": 5 }'
Screenshot
import { geekflarePost } from "./geekflare";
export interface Env {
GEEKFLARE_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { pathname } = new URL(request.url);
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
if (pathname === "/screenshot") {
const { url } = await request.json<{ url: string }>();
if (!url) {
return Response.json({ error: "url is required" }, { status: 400 });
}
try {
const result = await geekflarePost(env, "/screenshot", {
url,
type: "png",
fullPage: true,
device: "desktop",
viewportWidth: 1280,
viewportHeight: 800,
blockAds: true,
hideCookie: true,
quality: 90,
});
return Response.json(result);
} catch (error: any) {
return Response.json({ error: error.message }, { status: 500 });
}
}
return new Response("Not Found", { status: 404 });
},
};
Complete multi-route Worker
A single Worker handling all three endpoints:
import { geekflarePost } from "./geekflare";
export interface Env {
GEEKFLARE_API_KEY: string;
}
type RouteHandler = (request: Request, env: Env) => Promise<Response>;
const routes: Record<string, RouteHandler> = {
"/scrape": async (req, env) => {
const {
url,
renderJS = true,
blockAds = true,
} = await req.json<{
url: string;
renderJS?: boolean;
blockAds?: boolean;
}>();
if (!url)
return Response.json({ error: "url is required" }, { status: 400 });
const result = await geekflarePost(env, "/webscraping", {
url,
renderJS,
blockAds,
format: "html,markdown",
});
return Response.json(result);
},
"/search": async (req, env) => {
const {
query,
limit = 10,
location = "us",
} = await req.json<{ query: string; limit?: number; location?: string }>();
if (!query)
return Response.json({ error: "query is required" }, { status: 400 });
const result = await geekflarePost(env, "/search", {
query,
limit,
location,
source: "web",
format: "json",
});
return Response.json(result);
},
"/screenshot": async (req, env) => {
const { url } = await req.json<{ url: string }>();
if (!url)
return Response.json({ error: "url is required" }, { status: 400 });
const result = await geekflarePost(env, "/screenshot", {
url,
type: "png",
fullPage: true,
device: "desktop",
viewportWidth: 1280,
viewportHeight: 800,
blockAds: true,
hideCookie: true,
quality: 90,
});
return Response.json(result);
},
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const { pathname } = new URL(request.url);
const handler = routes[pathname];
if (!handler) {
return new Response("Not Found", { status: 404 });
}
try {
return await handler(request, env);
} catch (error: any) {
return Response.json({ error: error.message }, { status: 500 });
}
},
};
Deploy
Error handling
| Code | Meaning |
|---|
401 | Missing or invalid x-api-key |
402 | API credits exhausted |
403 | Endpoint not available on your plan |
429 | Rate limit exceeded |
The geekflarePost helper above throws on non-2xx responses. Wrap every handler in try/catch and map error codes to appropriate HTTP responses:
try {
const result = await geekflarePost(env, "/webscraping", { url });
return Response.json(result);
} catch (error: any) {
const message: string = error.message ?? "Unknown error";
if (message.startsWith("[429]")) {
return Response.json({ error: "Rate limit exceeded" }, { status: 429 });
}
if (message.startsWith("[402]")) {
return Response.json({ error: "Credits exhausted" }, { status: 402 });
}
return Response.json({ error: message }, { status: 500 });
}
Next steps