# How to Integrate Bolt.new with Serpstat

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

Integrate Serpstat with Bolt.new using its REST API through a Next.js API route that keeps your token server-side. Fetch keyword search volumes, difficulty scores, SERP competitor data, and domain analytics for any keyword or URL. Serpstat's API uses simple token authentication over HTTPS — all calls work from Bolt's WebContainer preview. Build keyword research dashboards, competitor analysis tools, and SEO reporting portals at a fraction of Ahrefs or SEMrush API pricing.

## Build Affordable Keyword Research Tools in Your Bolt.new App with Serpstat

Serpstat sits in a valuable middle ground in the SEO data market: it provides comprehensive keyword research, SERP analysis, and competitor intelligence at API pricing that's significantly more accessible than Ahrefs or SEMrush. For indie developers, agencies with tighter budgets, and SaaS tools that need SEO data as a feature rather than the core product, Serpstat offers a practical path to rich keyword data without the enterprise price tag.

The Serpstat API covers four main data categories that power most SEO use cases. Keyword data returns search volume, keyword difficulty (KD — a 0-100 scale predicting how hard it is to rank), cost-per-click (CPC for paid search estimates), competition level, and SERP features for any keyword. Domain keyword data shows which keywords a specific domain ranks for, making it essential for competitor research — you can see the entire organic keyword footprint of any competitor. SERP analysis returns the current top-10 results for any keyword, including each result's URL, title, and Serpstat's own authority metrics. Backlink data provides link counts and referring domain metrics for any URL.

All Serpstat API endpoints are standard HTTPS REST calls with a simple API token authentication model — the token is passed as a query parameter or header on every request. No OAuth flows, no token expiry, no refresh logic. This simplicity makes Serpstat one of the easiest SEO APIs to integrate, and since all calls are outbound HTTPS requests, the entire integration can be built and tested in Bolt's WebContainer preview without any deployment required.

## Before you start

- A Serpstat account at serpstat.com (paid plan required for API access — the free trial includes limited API credits)
- Serpstat API token from your account settings (serpstat.com → Profile → API)
- A Bolt.new project with Next.js for API routes
- Optional: Supabase for caching Serpstat API results to reduce API credit consumption

## Step-by-step guide

### 1. Get Your Serpstat API Token

Serpstat API access requires a paid plan — the free Serpstat account does not include API credits. If you have a Serpstat subscription, log in to your account at serpstat.com. Navigate to your profile settings: click your account name or avatar in the top right, then go to 'Profile' or 'Account Settings'. Find the 'API' section — some Serpstat interfaces show it as a direct menu item. Your API token is a long alphanumeric string. Copy it. The API token authenticates all your Serpstat API requests. It's a single static token (no OAuth, no expiry under normal circumstances) that you include in every API request as a `token` query parameter. Serpstat's API base URL is `https://api.serpstat.com/v4/`. The v4 API supports JSON request bodies and returns JSON responses. Each API call consumes API credits from your Serpstat subscription. Different endpoints consume different credit amounts — keyword data for a single keyword typically consumes fewer credits than domain analysis returning many keywords. Your remaining credit balance is shown in your Serpstat account dashboard and is also returned in API responses as a field you can track programmatically. Store your API token in Bolt's `.env` file as a server-side-only variable — never expose it in client-side code where users could extract it from browser DevTools and consume your API credits.

```
# .env — Serpstat API credentials
# Found in serpstat.com → Profile → API
SERPSTAT_API_TOKEN=your-serpstat-api-token

# Serpstat v4 API base URL
SERPSTAT_API_BASE=https://api.serpstat.com/v4

# Default search engine database (se = search engine)
# Use 'g_us' for Google US, 'g_uk' for Google UK, etc.
SERPSTAT_DEFAULT_SE=g_us

# For Next.js server-side: process.env.SERPSTAT_API_TOKEN
# NEVER use NEXT_PUBLIC_ prefix — this is a server-side credential only
```

**Expected result:** You have your Serpstat API token stored in .env. Verify it works by calling the Serpstat API manually from your API route with a simple keyword like 'seo tools'.

### 2. Create the Keyword Data API Route

The Serpstat keyword data endpoint is the foundation of most SEO tools you'll build. It returns comprehensive metrics for any keyword in any supported search engine: monthly search volume, keyword difficulty (KD, a 0-100 scale where 100 is hardest), CPC in USD, competition level (0-1 scale based on advertiser competition), and the number of SERP results. The Serpstat v4 API uses POST requests with a JSON body specifying the method (which API function to call), the keyword, the search engine database (e.g., 'g_us' for Google US), and your API token. The method for keyword data is `SerpstatKeyword.getKeywordsInfo`. Build an API route that accepts a keyword (or array of keywords) from your frontend, calls the Serpstat API, and returns the cleaned metric data. Include error handling for missing keywords, invalid tokens, and API credit exhaustion. The response from Serpstat's API includes a `status_code` field (200 for success), a `data` object with the keyword metrics, and `left_lines` showing your remaining API credit balance. Log the remaining balance in development to track usage. Since all calls are standard HTTPS requests to Serpstat's API, they work perfectly from Bolt's WebContainer preview — build and test the keyword research feature in the preview without deploying.

```
// app/api/serpstat/keyword-data/route.ts
import { NextResponse } from 'next/server';

const SERPSTAT_API_BASE = process.env.SERPSTAT_API_BASE ?? 'https://api.serpstat.com/v4';

export async function POST(request: Request) {
  const token = process.env.SERPSTAT_API_TOKEN;
  if (!token) {
    return NextResponse.json({ error: 'SERPSTAT_API_TOKEN not configured' }, { status: 500 });
  }

  const { keyword, se = process.env.SERPSTAT_DEFAULT_SE ?? 'g_us' } = await request.json();

  if (!keyword) {
    return NextResponse.json({ error: 'keyword is required' }, { status: 400 });
  }

  const serpstatResponse = await fetch(`${SERPSTAT_API_BASE}/?token=${token}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      id: '1',
      method: 'SerpstatKeyword.getKeywordsInfo',
      params: {
        se,
        keywords: [keyword],
      },
    }),
  });

  if (!serpstatResponse.ok) {
    return NextResponse.json(
      { error: `Serpstat API error: ${serpstatResponse.status}` },
      { status: serpstatResponse.status }
    );
  }

  const data = await serpstatResponse.json();

  if (data.error) {
    return NextResponse.json(
      { error: data.error.message ?? 'Serpstat API error' },
      { status: 400 }
    );
  }

  const keywordData = data.result?.data?.[keyword];

  if (!keywordData) {
    return NextResponse.json({
      keyword,
      found: false,
      message: 'No data found for this keyword in the selected search engine',
    });
  }

  return NextResponse.json({
    keyword,
    found: true,
    search_volume: keywordData.volume ?? 0,
    keyword_difficulty: keywordData.difficulty ?? 0,
    cpc: keywordData.cpc ?? 0,
    competition: keywordData.competition ?? 0,
    results_count: keywordData.results ?? 0,
    left_credits: data.result?.summary?.left_lines ?? null,
  });
}
```

**Expected result:** POST to /api/serpstat/keyword-data with { keyword: 'bolt new tutorial' } returns search volume, keyword difficulty, CPC, and competition data. left_credits in the response shows your remaining API balance.

### 3. Add Domain Keyword Analysis for Competitor Research

Serpstat's domain keyword endpoints reveal the complete organic keyword footprint of any website — which keywords they rank for, at what positions, and what traffic they receive from each keyword. This is one of the most valuable features for competitive research: understanding what keywords drive your competitors' organic traffic and identifying opportunities where they rank that you don't. The Serpstat method `SerpstatDomain.getKeywords` accepts a domain (without protocol), a search engine database, and pagination parameters. It returns an array of keyword objects, each with the keyword, the domain's ranking position, monthly search volume, keyword difficulty, CPC, and Serpstat's estimated traffic contribution. Build an API route for domain keyword analysis that accepts a domain and returns the top keywords sorted by traffic. Implement pagination support since competitors may rank for thousands of keywords — start with the first 20-50 by traffic for overview dashboards, and add pagination for deep-dive analysis. Cache the results in Supabase since a domain's keyword rankings don't change dramatically hour-to-hour — a 6-hour or 24-hour cache TTL reduces credit consumption significantly while keeping data reasonably fresh for research purposes.

```
// app/api/serpstat/domain-keywords/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';

const SERPSTAT_API_BASE = process.env.SERPSTAT_API_BASE ?? 'https://api.serpstat.com/v4';

function cleanDomain(input: string): string {
  return input
    .replace(/^https?:\/\//i, '')
    .replace(/^www\./i, '')
    .replace(/\/.*$/, '')
    .trim();
}

export async function POST(request: Request) {
  const token = process.env.SERPSTAT_API_TOKEN;
  if (!token) {
    return NextResponse.json({ error: 'SERPSTAT_API_TOKEN not configured' }, { status: 500 });
  }

  const { domain, se = 'g_us', limit = 20, minPosition = 10 } = await request.json();

  if (!domain) {
    return NextResponse.json({ error: 'domain is required' }, { status: 400 });
  }

  const cleanedDomain = cleanDomain(domain);
  const cacheKey = `${cleanedDomain}:${se}`;

  const supabase = createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.SUPABASE_SERVICE_ROLE_KEY!
  );

  // Check 6-hour cache
  const sixHoursAgo = new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString();
  const { data: cached } = await supabase
    .from('serpstat_domain_cache')
    .select('keywords_data, cached_at')
    .eq('cache_key', cacheKey)
    .gt('cached_at', sixHoursAgo)
    .single();

  if (cached) {
    const keywords = cached.keywords_data as Array<Record<string, unknown>>;
    return NextResponse.json({
      domain: cleanedDomain,
      keywords: keywords.filter((k) => (k.position as number) <= minPosition).slice(0, limit),
      cached: true,
    });
  }

  const serpstatResponse = await fetch(`${SERPSTAT_API_BASE}/?token=${token}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      id: '1',
      method: 'SerpstatDomain.getKeywords',
      params: {
        se,
        domain: cleanedDomain,
        sort: { traffic: 'desc' },
        page: 1,
        size: 100, // fetch more, filter client-side
      },
    }),
  });

  const data = await serpstatResponse.json();

  if (data.error) {
    return NextResponse.json({ error: data.error.message }, { status: 400 });
  }

  const keywords = (data.result?.data ?? []).map((item: {
    keyword: string;
    position: number;
    volume: number;
    difficulty: number;
    cpc: number;
    traff: number;
  }) => ({
    keyword: item.keyword,
    position: item.position,
    search_volume: item.volume ?? 0,
    keyword_difficulty: item.difficulty ?? 0,
    cpc: item.cpc ?? 0,
    traffic_estimate: item.traff ?? 0,
  }));

  // Cache results
  await supabase
    .from('serpstat_domain_cache')
    .upsert({
      cache_key: cacheKey,
      domain: cleanedDomain,
      se,
      keywords_data: keywords,
      cached_at: new Date().toISOString(),
    }, { onConflict: 'cache_key' });

  const filtered = keywords
    .filter((k: { position: number }) => k.position <= minPosition)
    .slice(0, limit);

  return NextResponse.json({
    domain: cleanedDomain,
    keywords: filtered,
    total_found: keywords.length,
    cached: false,
  });
}
```

**Expected result:** POST to /api/serpstat/domain-keywords with { domain: 'competitor.com' } returns the competitor's top keywords by traffic, with positions, volumes, and difficulty scores.

### 4. Build a SERP Competitor Analysis Feature

The SERP analysis endpoint returns the current top-10 organic search results for any keyword — who ranks there, with what URLs, and Serpstat's authority metrics for each result. This is powerful for two use cases: understanding what type of content Google rewards for a target keyword (blog post vs product page vs comparison article), and identifying which sites consistently appear for multiple keywords in your research, signaling strong competitors in your niche. The Serpstat method for SERP data is `SerpstatKeyword.getSerpResults`. It returns an array of ranking pages, each with the URL, title, domain, estimated organic traffic, and Serpstat's own domain analytics metrics. Build a SERP analysis component that shows the top results as cards, highlights any URLs from a target domain (for rank tracking), and groups results by content type using simple heuristics (URLs containing 'blog/', 'guide/', 'review' suggest informational content; '/product/', '/shop/', '/buy' suggest commercial intent). This feature requires the same API route authentication pattern as the keyword data endpoint — all Serpstat calls are outbound HTTPS requests that work from the WebContainer. The SERP results are particularly valuable when combined with keyword difficulty scores: a low-KD keyword with top results from weak domains signals an easy ranking opportunity.

```
// app/api/serpstat/serp/route.ts
import { NextResponse } from 'next/server';

const SERPSTAT_API_BASE = process.env.SERPSTAT_API_BASE ?? 'https://api.serpstat.com/v4';

export async function POST(request: Request) {
  const token = process.env.SERPSTAT_API_TOKEN;
  if (!token) {
    return NextResponse.json({ error: 'SERPSTAT_API_TOKEN not configured' }, { status: 500 });
  }

  const {
    keyword,
    se = 'g_us',
    target_domain = '',
  } = await request.json();

  if (!keyword) {
    return NextResponse.json({ error: 'keyword is required' }, { status: 400 });
  }

  const serpstatResponse = await fetch(`${SERPSTAT_API_BASE}/?token=${token}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      id: '1',
      method: 'SerpstatKeyword.getSerpResults',
      params: { se, keyword },
    }),
  });

  const data = await serpstatResponse.json();

  if (data.error) {
    return NextResponse.json({ error: data.error.message }, { status: 400 });
  }

  const cleanedTarget = target_domain
    .replace(/^https?:\/\//i, '')
    .replace(/^www\./i, '')
    .replace(/\/.*$/, '')
    .toLowerCase();

  const results = (data.result?.data ?? []).map((item: {
    position: number;
    url: string;
    title: string;
    domain: string;
    domain_rank: number;
    traff: number;
  }) => ({
    position: item.position,
    url: item.url,
    title: item.title ?? '',
    domain: item.domain,
    domain_authority: item.domain_rank ?? 0,
    traffic_estimate: item.traff ?? 0,
    is_target: cleanedTarget
      ? item.domain.toLowerCase().includes(cleanedTarget)
      : false,
  }));

  const targetPosition = results.find((r: { is_target: boolean }) => r.is_target)?.position ?? null;

  return NextResponse.json({
    keyword,
    se,
    results,
    target_position: targetPosition,
    target_found: targetPosition !== null,
  });
}
```

**Expected result:** POST to /api/serpstat/serp with { keyword: 'best seo tools', target_domain: 'mysite.com' } returns the top 10 SERP results with domain authority scores and highlights whether your domain appears.

## Best practices

- Cache all Serpstat API responses in Supabase — keyword data changes slowly (monthly at most), so a 24-hour cache dramatically reduces API credit consumption
- Never expose SERPSTAT_API_TOKEN in client-side code — all Serpstat calls must go through server-side API routes to protect your credit balance
- Normalize domain inputs before sending to the domain keywords endpoint — strip https://, www., and trailing slashes for consistent results
- Use Serpstat's batch keyword endpoint (passing multiple keywords in one request) rather than individual calls when researching keyword lists
- Monitor your remaining API credits (returned as left_lines in responses) and add logging to track usage — unexpected credit depletion can disable your tool
- Use appropriate search engine database codes for your target market — g_us for US Google, g_uk for UK Google, y_us for Bing, etc.
- Combine keyword difficulty with SERP analysis: check both the KD score and the actual domain authorities of top results before assessing ranking difficulty

## Use cases

### Keyword Research Dashboard

Build an internal tool where your team can enter any keyword and see instant data: search volume, keyword difficulty, CPC, top competing pages, and related keyword suggestions. Saves time compared to manually checking each keyword in Serpstat's interface.

Prompt example:

```
Create a keyword research dashboard at /keyword-research. Add a keyword input and search button. On submit, call /api/serpstat/keyword-data with the keyword and return: monthly search volume, keyword difficulty score (0-100), CPC estimate, competition level, and the top 10 SERP results with their URLs and titles. Display results with color coding (red for KD 70+, yellow for 40-70, green for under 40). Also show 5 related keyword suggestions.
```

### Competitor Keyword Gap Analysis

Find keywords your competitor ranks for that you don't. Enter a competitor's domain to see their top organic keywords by traffic, then identify which ones you should target. A classic SEO content strategy research tool.

Prompt example:

```
Build a competitor keyword analysis page. Accept a competitor domain URL (e.g., competitor.com). Call /api/serpstat/domain-keywords with the domain and return the top 20 keywords by organic traffic where the domain ranks in positions 1-10. Show each keyword with: position, monthly search volume, keyword difficulty, and traffic estimate. Sort by traffic descending. Add a 'KD under 50' filter to surface low-difficulty opportunities.
```

### SERP Tracking and Reporting Tool

Build a keyword position tracking report for your clients. For a list of tracked keywords, pull the current SERP top-10 results from Serpstat and show whether your client's domain appears and at what position. Export to CSV for client reports.

Prompt example:

```
Create a SERP tracking report page. Accept a list of keywords (one per line) and a target domain. For each keyword, call the Serpstat SERP analysis API and check if the target domain appears in the top 10 results. Return a table showing: keyword, current position (or 'Not ranking'), search volume, and the top 3 competitor URLs. Add an 'Export to CSV' button that downloads the full report.
```

## Troubleshooting

### Serpstat API returns error code -32600 or 'Invalid request'

Cause: The JSON-RPC request body format is incorrect — Serpstat uses JSON-RPC 2.0 format with id, method, and params fields. Missing the method or params fields causes this error.

Solution: Verify your request body matches the JSON-RPC format exactly: { id: '1', method: 'SerpstatKeyword.getKeywordsInfo', params: { se: 'g_us', keywords: ['your keyword'] } }. The id field is required (any string works). Check that the method name matches exactly — Serpstat method names are case-sensitive.

```
// Correct JSON-RPC request format for Serpstat
body: JSON.stringify({
  id: '1',  // Required - any string ID
  method: 'SerpstatKeyword.getKeywordsInfo',  // Exact method name
  params: {
    se: 'g_us',
    keywords: [keyword],  // Array, not a single string
  },
})
```

### Serpstat API returns error 'Token is not valid' or 401

Cause: The API token is incorrect, the token has been reset in the Serpstat account, or the token is being passed incorrectly in the request URL.

Solution: Verify SERPSTAT_API_TOKEN in your .env matches the token shown in serpstat.com → Profile → API. The token must be passed as a URL query parameter: `?token=your-token` appended to the API base URL. Check for extra spaces or newlines in the token value.

```
// Token goes in the URL query string, not in the request body
const url = `${SERPSTAT_API_BASE}/?token=${process.env.SERPSTAT_API_TOKEN}`;
const response = await fetch(url, { method: 'POST', body: JSON.stringify(payload) });
```

### Keyword data returns empty result or null for a common keyword

Cause: The keyword or search engine database combination has no data in Serpstat's index. Serpstat's database coverage varies by country and language — some long-tail keywords in specific regional databases may return no results.

Solution: Try the keyword with the 'g_us' database first (Google US has the most comprehensive coverage). For keywords in other countries, use the appropriate database code: 'g_uk' for UK, 'g_de' for Germany, etc. Very new or extremely niche keywords may not be indexed in Serpstat.

## Frequently asked questions

### How do I get a Serpstat API token?

Log in to your Serpstat account at serpstat.com and navigate to your Profile or Account Settings. Look for the 'API' section — your API token is shown there as a long alphanumeric string. Note that API access requires a paid Serpstat subscription; the free trial includes limited API credits but the token is immediately available after signing up.

### Can I call the Serpstat API from Bolt's WebContainer preview without deploying?

Yes. Serpstat's API is a standard HTTPS REST API — all calls are outbound from your API route to Serpstat's servers. Bolt's WebContainer supports outbound HTTPS requests, so you can build and test the full Serpstat integration in the preview. Serpstat has no webhooks or incoming connections, so deployment is not required at all for this integration.

### How does Serpstat compare to SEMrush and Ahrefs for a Bolt.new integration?

Serpstat, SEMrush, and Ahrefs all provide keyword and competitor data but differ significantly in price, data depth, and API structure. Serpstat's API is the most affordable of the three and uses simple token auth with JSON-RPC — straightforward to integrate. SEMrush and Ahrefs offer more data and better accuracy but at higher API costs. Choose Serpstat when budget is a constraint and keyword difficulty + SERP data meets your needs.

### What search engine databases does Serpstat support?

Serpstat supports Google databases for 230+ countries and Bing for US. Common database codes: 'g_us' (Google US), 'g_uk' (Google UK), 'g_de' (Google Germany), 'g_fr' (Google France), 'g_br' (Google Brazil), 'g_ru' (Google Russia), 'y_us' (Bing US). The full list is in Serpstat's API documentation. Specify the se parameter in your API calls to target the correct country database.

### How does Serpstat's JSON-RPC format work?

Serpstat v4 uses JSON-RPC 2.0 format for all requests — you POST to the same URL endpoint (?token=your-token) but specify which API method to call in the request body as { id: '1', method: 'MethodName', params: { ... } }. The id field is required but can be any string. The method field determines which data you're requesting (e.g., SerpstatKeyword.getKeywordsInfo, SerpstatDomain.getKeywords). This is different from typical REST APIs where the method is determined by the URL path.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/serpstat
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/serpstat
