> ## 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.

# Next.js

> Use Geekflare API in a Next.js application with Server Actions, Route Handlers, and Server Components.

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](https://dash.geekflare.com)

## Installation

<Tabs>
  <Tab title="pnpm">`bash pnpm add @geekflare/api-node `</Tab>
  <Tab title="npm">`bash npm install @geekflare/api-node `</Tab>
  <Tab title="yarn">`bash yarn add @geekflare/api-node `</Tab>
</Tabs>

## Set your API key

Add your API key to `.env.local`:

```bash .env.local theme={null}
GEEKFLARE_API_KEY=your_api_key_here
```

<Warning>
  `.env.local` is gitignored by default in Next.js. Never prefix this variable
  with `NEXT_PUBLIC_` — that would expose it to the browser.
</Warning>

## Shared client

Create a single client instance to reuse across your app:

```ts lib/geekflare.ts theme={null}
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.

```ts app/api/scrape/route.ts theme={null}
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:

```ts app/actions.ts theme={null}
"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:

```tsx app/scrape/page.tsx theme={null}
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

```ts app/api/search/route.ts theme={null}
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

```ts app/actions.ts theme={null}
"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

```tsx app/search/page.tsx theme={null}
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

```ts app/api/screenshot/route.ts theme={null}
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`:

```tsx app/preview/page.tsx theme={null}
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

```ts theme={null}
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

```ts theme={null}
"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:

```tsx theme={null}
const { data, error } = await safeScrape(url);

if (error) {
  return <p className="text-red-500">{error}</p>;
}
```

***

## Next steps

* [NestJS quickstart](/api/quickstart/nestjs) — Injectable services and modules
* [API Reference](/api/endpoint/reference) — Full endpoint documentation
* [SDK on npm](https://www.npmjs.com/package/@geekflare/api-node)
