Fiber AI
Build

Building UIs on the Fiber API

The pattern book for wrapping Fiber in your own web UI — autocomplete that resolves, live match counts, cursor-paginated tables, progress for slow jobs, and credits on screen. With toy Next.js examples.

Building UIs on the Fiber API

Everything the Fiber dashboard does runs on the same public API your key unlocks. We built the UI and the API on the same surface. A search-and-enrich interface of your own is therefore mostly composition: a handful of operations that map one-to-one onto UI elements, a server route that holds your key, and a way to surface results when the work is async. This page walks those patterns with toy examples, and OpenFiber is the same book at full scale.

Want a working app instead of a pattern book? We open-sourced one. OpenFiber is an MIT-licensed Next.js prospecting UI over ~180 operations — run it in five minutes, fork it, or strip it for parts.

What you're building

Six UI elements cover nearly every data surface on Fiber:

UI elementBacked byWatch out for
Filter autocompleteTypeaheads: companyTypeahead, locationTypeahead, skillsTypeahead, jobTitleRewriteAll four share 240 requests/minute
Live match countcompanyCount, peopleSearchCount, jobPostingSearchCount, combinedSearchCountEstimates cap at 10,000
Results tablecompanySearch, peopleSearch, paginatedCombinedSearchCursor-paginated; 1 credit per result
Reveal-contacts buttonsyncQuickContactRevealSync — returns inline
Bulk enrich with progressstartBatchContactDetails → pollBatchContactDetailsAsync; 10–2,000 identifiers per job
Credits metergetOrgCredits (free) + chargeInfo on every responseOne entry per subscription

The one architectural rule: the key stays on the server

The browser never talks to api.fiber.ai directly. Every call flows browser → your server routes → Fiber, with the API key read from the server environment. That keeps the key out of client bundles and network tabs, and gives you one place to add caching, per-user limits, or your own auth — which is also what makes a Fiber-powered UI safe to put behind your own login.

A toy Next.js route handler that pages companies:

// app/api/companies/route.ts
import { companySearch } from "@fiberai/sdk";
import type { CompanySearchData } from "@fiberai/sdk";

export async function POST(req: Request) {
  const { searchParams, cursor } = (await req.json()) as {
    searchParams: CompanySearchData["body"]["searchParams"];
    cursor?: string;
  };

  const result = await companySearch({
    body: {
      apiKey: process.env.FIBER_API_KEY!,
      searchParams,
      pageSize: 25,
      cursor, // omit on the first page
    },
  });

  if (result.error) {
    return Response.json(result.error, {
      status: result.response.status,
    });
  }
  // output: { data, nextCursor, estimatedCount }
  return Response.json(result.data.output);
}

Every pattern below plugs into a route like this one. See SDKs for typed clients in TypeScript and Python.

Autocomplete resolves; free text guesses

Search filters expect canonical values, and the typeaheads return exactly those: companies (plus schools and investors via orgType), locations with coordinates and metro-region presets, professional skills, and job-title synonym families. Picking from the list is a deterministic match; typing "MIT" into an education filter is a gamble.

A toy debounced autocomplete:

const [suggestions, setSuggestions] = useState([]);
const [query, setQuery] = useState("");

useEffect(() => {
  if (query.length < 3) return; // don't fire on every keystroke
  const timer = setTimeout(async () => {
    const res = await fetch("/api/typeahead/company", {
      method: "POST",
      body: JSON.stringify({ startsWith: query }),
    });
    setSuggestions(await res.json());
  }, 300); // debounce behind a pause
  return () => clearTimeout(timer);
}, [query]);

Store what the user picked (the entity's slug, domain, or coordinates), not the string they typed. Skills typeahead is free at any frequency; the other three cost a credit per call, and all four share one 240 requests/minute budget, so debounce everything. Full behavior per endpoint: Typeaheads.

Match counts live in the filter UI

Count endpoints answer "how many match?" without pulling a single record. They return { output: { count } }, small enough to refresh on every filter change. Show the number next to the Search button ("1,240 companies match") and users tune filters against feedback instead of guessing, then commit once.

Counts are estimates capped at 10,000 — display "10,000+" at the cap, not a fake total. A capped count means "keep narrowing", not "exactly 10,000". Pricing detail: count before you spend.

Results tables page with cursors

Ask for up to 1,000 results per page (pageSize, default 25); the response carries a nextCursor you pass back as cursor for the next page. A "Load more" button is just nextCursor !== null.

Two rules worth encoding in your table component:

  • A cursor belongs to one query. When the user changes filters, drop the cursor and start a fresh run — a recycled cursor quietly keeps serving the old query.
  • Set an explicit sort whenever you're showing a sample of a large set. Filter-only results have no inherent ranking, so a defined sort is the difference between a reproducible sample and an accidental one.

Slow jobs get progress, not spinners

Batch reveal, exhaustive enrichment, and Maps sweeps run in the background for anywhere from seconds to minutes. The UI pattern:

  1. Commit. The user picks rows; you start a batch job with 10–2,000 identifiers and store the returned job identifier.
  2. Show per-row state. Poll that job every few seconds and render each row as pending, done, or failed — results land as they finish instead of blocking the table.
  3. Or skip the polling. Subscribe to batch_contact_enrich.completed with a webhook, verify the signature, update your store, and let the UI react. Ack fast and process async — retries are built in, and deduping on the svix-id header keeps replays harmless.

Tier-by-tier sync/async behavior, including the instant single-profile button: Contact Reveal.

Make cost a first-class citizen

Two fields, no bookkeeping required:

  • chargeInfo on every response is what that call actually cost. Show it where results appear ("this page: 25 credits"). It is authoritative over any published rate.
  • The balance read is free and returns available per subscription. Render a credits meter and refresh it after chargeable actions.

The error states a production UI should handle explicitly:

StatusMeaningUI response
402Org out of creditsPause jobs, link the purchase URL from the response
429Rate limit hitBack off and retry silently — users shouldn't see it
403Key invalid or over its ceilingSurface once, stop retrying
400Malformed requestA bug in your payload — log it, don't retry

Full error table and per-key spending ceilings: SDKs and API keys.

Start from the demo instead

Every pattern above is already assembled in OpenFiber — typeahead-driven filters with live match counts, cursor-paginated tables, batch jobs with progress, a credits meter — over ~180 operations, MIT-licensed:

git clone https://github.com/fiber-ai/open-fiber.git
cd open-fiber/nextjs
cp .env.example .env   # set FIBER_API_KEY
npm install
npm run dev

Adding a feature is a four-step seam (page, server router wrapping the SDK operation, registration, nav entry), and a rebrand touches about five files. The details: OpenFiber.

Use cases

  • Internal prospecting tool. Company filters with live counts, people search behind the same panel, batch reveal with per-row progress — the six elements above, wired end to end.
  • Enrichment panel in your CRM. A reveal button per record, batch for CSV imports, the credits meter in the corner of the settings page.
  • Market-sizing widget. Count endpoints only: "how many Series A fintechs in Germany" answers without spending search credits.

Credits

There is no UI-specific pricing. A call from your UI bills exactly like the same call from anywhere else, and chargeInfo on each response stays authoritative. See Billing & credits.

Related: OpenFiber · SDKs · Search · Typeaheads · Contact Reveal · Webhooks

On this page