# Serpstat

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Serpstat by creating an API Group with base URL https://api.serpstat.com/v4 and a ?token= query parameter, then using a single POST call whose JSON body sets the 'method' field to select the report type — this is Serpstat's JSON-RPC design. Route the token through a Firebase Cloud Function proxy to keep it off the client, and cache results in App State to stay within monthly credit limits.

## Build an Affordable SEO App with Serpstat's Single-Endpoint API in FlutterFlow

Serpstat is the value pick in the SEO API market — it offers keyword research, domain analytics, and backlink data at price points well below Ahrefs (which requires an Enterprise plan) or SEMrush (which charges per unit on top of a seat subscription). For FlutterFlow developers building an SEO dashboard or keyword research companion app, Serpstat's lower cost makes it the practical choice for apps where the API will be called frequently.

What sets Serpstat apart architecturally is its JSON-RPC design: there is exactly one API endpoint (https://api.serpstat.com/v4), and you select which report to fetch by setting a 'method' field in the POST body — for example, 'SerpstatDomainProcedure.getDomainKeywords' for keyword data or 'SerpstatDomainProcedure.getDomainInfo' for domain metrics. The 'params' body field carries the report parameters (domain, se — search engine/country database). This pattern is fundamentally different from the other SEO APIs in this category, which use distinct URL paths per report. In FlutterFlow it means you build ONE API Call and control which report runs by changing the body variables — a clean, reusable design.

Authentication uses a token passed as a ?token= query parameter appended to the URL. Like SEMrush's ?key= pattern, query strings appear in network logs and are embedded in the compiled app binary, making proxy routing essential. Serpstat also enforces both a per-second request rate limit and a monthly credit/unit quota — so the same unit-conservation pattern applies: cache in App State, refresh on demand, add retry backoff in the proxy for 429 responses.

## Before you start

- A Serpstat account with API access enabled (API is available on paid Serpstat plans; verify which plan includes API credits)
- Your Serpstat API token from the Serpstat dashboard → Profile → API
- A FlutterFlow project (free or paid) with a screen ready to display keyword or domain data
- A Firebase project for the Cloud Function proxy that holds the Serpstat token and handles 429 retry logic
- Basic familiarity with FlutterFlow's API Calls panel; understanding of POST requests with JSON bodies is helpful

## Step-by-step guide

### 1. Deploy a Firebase Cloud Function proxy with 429 backoff

Serpstat's token goes in the URL query string (?token=...), which means it appears in HTTP access logs at any intermediate point and is embedded in the compiled Flutter binary if placed in a FlutterFlow API Call variable. The Cloud Function proxy solves this and also provides the 429 backoff that FlutterFlow API Calls cannot do natively — FlutterFlow has no built-in retry mechanism, so hammering Serpstat when the per-second limit is exceeded would silently fail without a retry.

Create an HTTP Cloud Function. The function receives the method and params from FlutterFlow in its POST body, reads the token from process.env.SERPSTAT_TOKEN, builds the Serpstat v4 URL with the token appended, and POSTs the JSON-RPC body to it. Add simple 429 retry logic: if Serpstat returns 429, wait 1 second and retry once before returning the error to FlutterFlow.

Set the token: firebase functions:config:set serpstat.token='YOUR_TOKEN'. Add Access-Control-Allow-Origin: * headers for FlutterFlow web builds. Deploy with firebase deploy --only functions. Test by POSTing {"method": "SerpstatDomainProcedure.getDomainInfo", "params": {"domain": "example.com", "se": "g_us"}} to your function URL and confirming you get a JSON response with result.data.

```
// Firebase Cloud Function proxy for Serpstat (functions/index.js)
const functions = require('firebase-functions');
const axios     = require('axios');

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

exports.serpstatProxy = functions.https.onRequest(async (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
  res.set('Access-Control-Allow-Headers', 'Content-Type');
  if (req.method === 'OPTIONS') { res.status(204).send(''); return; }
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'POST required' });
  }

  const token  = process.env.SERPSTAT_TOKEN;
  const method = req.body?.method;
  const params = req.body?.params || {};

  if (!method) {
    return res.status(400).json({ error: 'method is required' });
  }

  const url = `https://api.serpstat.com/v4?token=${token}`;
  const body = { id: 1, method, params };

  const doRequest = async (attempt) => {
    try {
      const response = await axios.post(url, body, {
        headers: { 'Content-Type': 'application/json' },
        timeout: 15000
      });
      return response.data;
    } catch (err) {
      if (err.response?.status === 429 && attempt === 1) {
        await sleep(1000);
        return doRequest(2);
      }
      throw err;
    }
  };

  try {
    const data = await doRequest(1);
    res.json(data);
  } catch (err) {
    const status = err.response?.status || 500;
    res.status(status).json({ error: err.message });
  }
});
```

**Expected result:** The Cloud Function is deployed and returns Serpstat JSON with result.data when you POST a valid method and params to its URL.

### 2. Create the Serpstat API Group in FlutterFlow

In FlutterFlow, navigate to API Calls in the left nav. Click + Add → Create API Group. Name it Serpstat. In the Base URL field, paste your Cloud Function URL: https://us-central1-YOUR_PROJECT.cloudfunctions.net/serpstatProxy.

Since your proxy handles authentication internally, you do not need any Authorization headers at the group level. However, you may want to add an optional X-App-Key header with a static secret UUID that your Cloud Function checks — this restricts your proxy so only your FlutterFlow app can call it, rather than anyone who discovers the Cloud Function URL.

Save the group. The Serpstat group now appears in the left nav, ready for API Call definitions. The key insight here is that unlike Ahrefs (separate calls per endpoint) or Moz (one endpoint, one purpose), you will create just ONE API Call inside this group — because Serpstat's single URL handles all report types via the method body field.

**Expected result:** The Serpstat API Group is saved and visible in the left nav with your Cloud Function URL as the base URL.

### 3. Add the single multi-purpose POST API Call

Because Serpstat uses a JSON-RPC design, you only need one API Call. The method field in the body determines which report runs. You will pass method and params as FlutterFlow body Variables, making this call reusable across different screens with different report needs.

Click + inside the Serpstat group → Create API Call. Name it Serpstat Query. Set the HTTP Method to POST. Set the Body Type to JSON.

Go to the Variables tab and add:
  - method (String, required) — e.g. 'SerpstatDomainProcedure.getDomainInfo'
  - domain (String, required) — the domain to query
  - se (String, default: g_us) — the search engine/country database (g_us, g_uk, g_de, etc.)
  - limit (Integer, default: 100) — max rows of keyword data to return

In the Body field, use this JSON template:
{"method": "{{method}}", "params": {"domain": "{{domain}}", "se": "{{se}}", "size": {{limit}}}}

Note: for keyword methods (SerpstatKeywordProcedure.getKeywordInfo), replace 'domain' in params with 'keyword'. You can handle this by adding a keyword variable and building two API Calls (one for domain methods, one for keyword methods) — or use a single flexible call with both variables and let the proxy pick the right one.

Switch to Response & Test. Fill in method=SerpstatDomainProcedure.getDomainInfo, domain=example.com, se=g_us. Run the test. The JSON response will have a result.data object with fields like keyword_count, traff, traff_cost. Click Generate JSON Paths. Map: $.result.data.keyword_count → keywordCount, $.result.data.traff → estimatedTraffic, $.result.data.traff_cost → trafficCost.

```
// API Call POST body template
{
  "method": "{{method}}",
  "params": {
    "domain": "{{domain}}",
    "se": "{{se}}",
    "size": {{limit}}
  }
}

// Common Serpstat methods to use as the 'method' variable:
// Domain reports:
//   SerpstatDomainProcedure.getDomainInfo         — overall domain metrics
//   SerpstatDomainProcedure.getDomainKeywords     — organic keyword list
//   SerpstatDomainProcedure.getCompetitors        — domain competitors
// Keyword reports:
//   SerpstatKeywordProcedure.getKeywordInfo       — keyword volume + CPC
//   SerpstatKeywordProcedure.getRelatedKeywords   — related keywords list

// JSON Paths for getDomainInfo response:
// keywordCount    → $.result.data.keyword_count
// estimatedTraff  → $.result.data.traff
// trafficCost     → $.result.data.traff_cost
// visibilityScore → $.result.data.visible_percentage
```

**Expected result:** The Serpstat Query API Call is saved. The test panel returns domain metrics JSON when called with getDomainInfo. JSON Paths for keyword count and traffic are generated.

### 4. Parse nested JSON results and bind to widgets

Serpstat's responses nest data inside result.data — for domain methods it is a single object, and for keyword list methods (getDomainKeywords, getRelatedKeywords) it is an array at $.result.data[*]. FlutterFlow's JSON Path tool handles both, but you need to configure your JSON Paths correctly for each case.

For domain overview data (getDomainInfo): the JSON Path $.result.data.keyword_count extracts a single integer. Bind it directly to a Text widget on your dashboard card.

For keyword list data (getDomainKeywords): the JSON Path $.result.data[*].keyword extracts all keyword strings as a list, and $.result.data[*].traff extracts the corresponding traffic values. In FlutterFlow, create a custom Data Type called SerpstatKeyword with fields: keyword (String), traff (int), cost (double), keyword_length (int), concurrency (int). Use the API Call's JSON Path list extraction to generate a list of SerpstatKeyword items, then bind the list to a ListView whose list tile widgets display keyword name and volume.

On your domain overview screen, add a Row with three metric Cards: Keyword Count, Estimated Traffic, and Traffic Cost Value. Bind each to the relevant App State variable populated by the API Call action. For the keyword list screen, add a ListView bound to the list returned by the getDomainKeywords call, with each item showing the keyword and its traffic estimate. Add a search/filter bar above the ListView so users can narrow the keyword list without making a new API call.

**Expected result:** Domain metric cards show keyword count and estimated traffic from the App State. A keyword ListView shows individual keywords with their traffic values from the getDomainKeywords call.

### 5. Wire the full action flow and handle rate limits

Now connect all the pieces into a working app flow. On your main screen, add a TextField for domain input and a Search button. In the Search button's Actions panel:
  1. Validate the TextField is not empty.
  2. Set App State variable isLoading = true.
  3. Backend/API Call → Serpstat → Serpstat Query, with method = 'SerpstatDomainProcedure.getDomainInfo', domain = TextField value, se = currently selected database, limit = 100.
  4. On success: update App State with the parsed JSON Path values (keywordCount, estimatedTraffic, trafficCost). Set isLoading = false.
  5. On error: show a Dialog — if the error is a 429 response, display 'Serpstat rate limit reached — please wait a moment.' If it's a 401, display 'Invalid token — check your Serpstat API configuration.'

For unit conservation, add a lastSearched App State variable (String, stores the last-queried domain). At the start of the Search action, check if the domain TextField matches lastSearched AND the App State metrics are not null — if both are true, skip the API call and display the cached App State values with a toast 'Showing cached result'. This saves a credit on repeated searches for the same domain.

If you'd rather skip the custom action and proxy setup and have a FlutterFlow expert wire this for you, RapidDev's team builds integrations like this every week — free scoping call at rapidevelopers.com/contact.

**Expected result:** The full search flow works end to end: user enters a domain, taps Search, the proxy calls Serpstat, JSON is parsed, and domain metrics appear on screen. Cached results display on repeat searches without consuming additional credits.

## Best practices

- Never put the Serpstat token in a FlutterFlow query variable — the ?token= value is embedded in the compiled Dart binary and appears in server access logs. Always proxy through a Firebase Cloud Function with the token in environment variables.
- Implement 429 backoff in the Cloud Function proxy (not in FlutterFlow) since FlutterFlow API Calls have no built-in retry mechanism — one second of sleep before retrying is usually enough to satisfy the per-second rate limit.
- Cache the last domain-queried result in App State and skip re-fetching if the user searches the same domain again within a session — Serpstat credits are metered, and repeated identical calls waste quota.
- Use the Serpstat dashboard to monitor your monthly credit consumption regularly, especially during app development when you may be testing frequently.
- Expose the 'se' (search engine/country) parameter as a dropdown in your UI so users can switch regional databases without code changes — this is one of Serpstat's most useful features for international SEO teams.
- Check result.error in the JSON response body in addition to HTTP status codes — Serpstat returns 200 with an error in the payload for credit exhaustion and other quota issues.
- Use a single reusable Serpstat Query API Call for all report types (vary only the method and params variables) rather than creating separate API Calls per method — this keeps your API Calls panel clean and the proxy logic unified.

## Use cases

### Keyword research and volume lookup tool

Build a keyword research app where a user types a search phrase, selects a country database (US, UK, DE, etc.), and sees monthly volume, CPC, competition level, and related keyword suggestions from Serpstat. The app uses the SerpstatKeywordProcedure.getKeywordInfo method in the POST body and displays results in a card list. Results are stored in a local Supabase table for later review, avoiding repeated API calls for recently researched keywords.

Prompt example:

```
A keyword research app where a user types a keyword and selects a country, then sees monthly search volume, CPC, and competition level from Serpstat in a list of cards.
```

### Domain keyword and traffic overview dashboard

Build an internal SEO monitoring app that shows a domain's estimated organic keyword count and visibility score from Serpstat. Users enter a domain, the app calls SerpstatDomainProcedure.getDomainInfo via the proxy, and displays keyword_count, traff, and traff_cost on metric cards. A refresh button at the top right re-triggers the API call and updates the cached App State value.

Prompt example:

```
An SEO dashboard app where a user enters a domain and sees its organic keyword count, estimated traffic, and traffic cost value from Serpstat on a metrics card.
```

### Competitor keyword gap analysis screen

Build a comparison screen where a user enters two domains and the app fetches getDomainInfo for each using two sequential API calls through the same Serpstat proxy. The results display side by side on a split-card layout showing keyword count and traffic for both domains, highlighting the domain with more organic presence. The single-endpoint design means both calls use the exact same FlutterFlow API Call — only the params.domain variable differs.

Prompt example:

```
A competitor SEO comparison app that shows two domains side by side with their organic keyword count and estimated traffic from Serpstat.
```

## Troubleshooting

### API returns status 200 but result.data is empty or null

Cause: Serpstat returns status 200 even for 'no data' results. This happens when the domain has no indexed keywords in the selected search engine database ('se' parameter), or when the account's monthly credits are exhausted. Serpstat also returns error details in a result.error field rather than using HTTP error codes.

Solution: Check the result.error field in the JSON response in your Cloud Function logs. If it contains a credits error, your monthly quota is exhausted. If it contains 'no data', try a different 'se' (e.g. change g_uk to g_us). If credits are the issue, wait for the next billing cycle or upgrade your Serpstat plan.

### 429 Too Many Requests — API calls failing during testing

Cause: Serpstat enforces a per-second request rate limit. Rapid consecutive taps on the Search button, or a widget that triggers re-fetch on every rebuild, can exceed this limit even with a modest request volume.

Solution: Ensure the Cloud Function proxy has the 1-second retry backoff implemented as shown in Step 1. In FlutterFlow, disable the Search button while an API call is in flight (bind its disabled state to the isLoading App State variable) to prevent rapid duplicate taps. If the ListView refetches on scroll, move the data source from a widget API call to App State.

### XMLHttpRequest error in FlutterFlow Run mode when testing the API Call

Cause: CORS error. If the API Group's base URL points to api.serpstat.com instead of your Cloud Function, the browser blocks the cross-origin POST request. FlutterFlow's web-based Run mode enforces CORS; native mobile builds do not.

Solution: Confirm the API Group base URL is your Cloud Function URL (https://us-central1-YOUR_PROJECT.cloudfunctions.net/serpstatProxy). If you set it to api.serpstat.com for quick testing, update it to the proxy URL. The Cloud Function adds the necessary CORS headers so web runs work correctly.

### JSON Path $.result.data[*].keyword returns no results in FlutterFlow

Cause: For getDomainInfo, result.data is an object (not an array), so the [*] wildcard returns nothing. The [*] syntax is only correct for list methods like getDomainKeywords or getRelatedKeywords.

Solution: Use method-appropriate JSON Paths: $.result.data.keyword_count (no wildcard) for getDomainInfo, and $.result.data[*].keyword (with wildcard) for getDomainKeywords. Create separate API Call definitions or separate JSON Path sets for each method type. The FlutterFlow API Call Response & Test tab will show you the exact shape of the response when you run a test.

## Frequently asked questions

### What makes Serpstat's API different from Ahrefs or SEMrush?

Serpstat uses a JSON-RPC design: one endpoint URL, and you select the report by setting a 'method' field in the JSON POST body. Ahrefs uses separate URL paths per endpoint with a Bearer header, and SEMrush uses separate URL parameters with a query-string key and returns CSV. Serpstat's single-endpoint design means you configure one FlutterFlow API Call and reuse it for all report types by varying the body variables.

### Is there a free tier for the Serpstat API?

Serpstat API access is available on paid Serpstat plans. There is no permanently free API tier, though Serpstat offers trial periods. Check the current plan pricing at serpstat.com/plans — API credit allotments vary by plan level, and the per-API-call cost varies by report type. Domain overview calls are generally cheaper than large keyword list exports.

### Can I use Serpstat for both keyword research and domain analytics in the same FlutterFlow app?

Yes — this is one of Serpstat's advantages. Because all report types share the same endpoint, your single FlutterFlow API Call can fetch domain metrics, keyword lists, competitor domains, and related keyword suggestions simply by changing the 'method' body variable. Build different screens that all use the same Serpstat Query API Call with different method and params values.

### What does the 'se' parameter mean in Serpstat requests?

The 'se' (search engine) parameter selects the regional Google index to query — for example, g_us for Google US, g_uk for Google UK, g_de for Google Germany. Serpstat stores separate keyword databases per regional index, so the keyword volumes and domain rankings it returns are specific to that country's Google results. Always set 'se' to match your target market.

### Why is my Serpstat proxy returning 200 but the data is empty?

Serpstat returns HTTP 200 even when there is no data for a domain or when credits are exhausted. Check the result.error field inside the JSON response body — if credits are exhausted, this field contains an error message. If result.error is null but result.data is also empty, the domain has no indexed keywords in the selected regional database. Try switching the 'se' parameter to a different regional index.

---

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