Nearbubbles
Docs // integration

Embed the bubble map on your website

Show the Nearbubbles bubble map of any NEAR NEP-141 token on your own site with a single <iframe>: holders, clusters and the transfers between them, always current.

Access

For integration access contact @Hector885 on telegram.

You receive an API key tied to your website(s), with its own usage limits.

Quick start

HTML
<iframe
  src="https://nearbubbles.xyz/embed/wrap.near?key=YOUR_API_KEY"
  width="100%" height="600" style="border:0;border-radius:12px"
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"
  title="Nearbubbles bubble map"></iframe>
  1. Replace wrap.near with the token contract you want to show.
  2. Replace YOUR_API_KEY with your key.
  3. Open the page on the website registered for your key. The map loads with Powered by Nearbubbles at the bottom left; it links to the main website.

Tokens nobody mapped before are analysed on demand: the holders appear first and the connections follow while the scan finishes, usually well under a minute. Tokens that were mapped before open immediately.

URL and parameters

https://nearbubbles.xyz/embed/{tokenContractId}?key={apiKey}[&list=0]
PartRequiredDescription
{tokenContractId}yesNEAR account ID of the NEP-141 token, for example wrap.near, token.sweat or usdt.tether-token.near.
keyyesYour API key.
listno0 hides the Address List button and panel (map only). Default: shown.

Viewers can drag bubbles, zoom, hover for balance and share of supply, click a bubble or a line for details, open the address list and jump to the account on an explorer.

Size and layout

The map fills the iframe. Recommended minimum: 360 px high and 300 px wide. A responsive container with a fixed ratio:

HTML
<div style="position:relative;width:100%;aspect-ratio:16/10;min-height:360px">
  <iframe
    src="https://nearbubbles.xyz/embed/wrap.near?key=YOUR_API_KEY"
    style="position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:12px"
    loading="lazy" referrerpolicy="strict-origin-when-cross-origin"
    title="Nearbubbles bubble map"></iframe>
</div>

Choosing the token from your app

Build the URL from the token contract your page already knows.

JavaScript
function showBubbleMap(container, tokenContractId) {
  const iframe = document.createElement("iframe");
  iframe.src = `https://nearbubbles.xyz/embed/${encodeURIComponent(tokenContractId)}?key=YOUR_API_KEY`;
  iframe.style.cssText = "width:100%;height:600px;border:0;border-radius:12px";
  iframe.referrerPolicy = "strict-origin-when-cross-origin";
  iframe.title = "Nearbubbles bubble map";
  container.replaceChildren(iframe);
}
React
export function BubbleMap({ token }: { token: string }) {
  return (
    <iframe
      key={token}
      src={`https://nearbubbles.xyz/embed/${encodeURIComponent(token)}?key=${process.env.NEXT_PUBLIC_NEARBUBBLES_KEY}`}
      style={{ width: "100%", height: 600, border: 0, borderRadius: 12 }}
      referrerPolicy="strict-origin-when-cross-origin"
      loading="lazy"
      title="Nearbubbles bubble map"
    />
  );
}

Changing the iframe address (or the React key) loads the new token. Invalid contract IDs show an error inside the frame and cost no quota.

Your key and your website

A key only works on the websites registered with it, for example acme.com and *.acme.com. The check uses the address of the page that contains the iframe, which the browser sends automatically.

  • List the exact host of every page that embeds the map. *.acme.com covers app.acme.com but not acme.com itself, so list both when you need both. Ports and paths are ignored.
  • To test locally, ask us to add localhost to your key, and remove it later.
  • Do not set Referrer-Policy: no-referrer (or same-origin) on the page holding the iframe: the browser would hide your address and the key is refused. The browser default strict-origin-when-cross-origin works.
  • The key is visible in your page source because the browser has to send it. It is a publishable key, protected by the website list, rate limits and quotas. Never reuse it elsewhere. If it leaks, tell us: we rotate it and the old key stops working immediately.

If your site sends a Content-Security-Policy, allow the frame:

Content-Security-Policy: frame-src https://nearbubbles.xyz;

Limits

LimitMeaning
Maps per minuteHow many map requests the key may start per minute, across all of your visitors.
Maps per dayDaily quota, counted in UTC. Can be unlimited.
ExpiryOptional end date of the key.

Opening a token again counts again (a cached map is served instantly, but it is still one request). When a limit is reached the embed shows a message and recovers on its own: the minute limit after a minute, the daily quota at 00:00 UTC. Need more? Contact us.

Server-to-server API

Keys with API access can fetch the map as JSON from your backend. Do not call it from a browser (the key would be exposed and cross-origin calls are blocked); use the iframe for anything a visitor sees.

Start or join a map
curl -X POST https://nearbubbles.xyz/api/v1/maps \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"contractId":"wrap.near"}'
  • 200 with "status":"completed": the map is in map.
  • 202 with "status":"queued" or "processing": poll the job. The body has progressPercent, message, pollAfterMs and, while connections are still scanned, a previewMap with the verified holders.
Poll a job every pollAfterMs (about 2 s)
curl https://nearbubbles.xyz/api/v1/maps/JOB_ID -H "X-API-Key: YOUR_API_KEY"
Completed response (shortened)
{
  "status": "completed",
  "jobId": "…",
  "contractId": "wrap.near",
  "cachedAt": "2026-10-20T12:00:00.000Z",
  "map": {
    "token": { "name": "Wrapped NEAR", "symbol": "wNEAR", "decimals": 24, "totalSupplyDisplay": "…" },
    "snapshot": { "blockHeight": 123456789, "generatedAt": "…", "candidateCount": 100, "eligibleHolderCount": 41 },
    "holders": [
      { "accountId": "…", "balanceDisplay": "…", "percent": "12.345678", "clusterId": "c1", "connectionCount": 3 }
    ],
    "edges": [
      { "id": "a.near|b.near", "source": "a.near", "target": "b.near", "transferCount": 4, "aToBCount": 3, "bToACount": 1 }
    ],
    "clusters": [{ "clusterId": "c1", "memberCount": 5, "clusterSupplyPercent": "20.1" }]
  }
}

percent values are strings in percent units ("12.345678" is 12.345678 %). Amounts are decimal strings. Failed jobs return "status":"failed" with error: { code, message, retryable }.

Error codes

HTTPcodeMeaning
400INVALID_CONTRACT_IDThe token ID is not a valid NEAR account ID.
401KEY_INVALIDMissing or unknown API key.
403KEY_INVALIDKey is paused, revoked or expired, or has no API access.
429KEY_RATE_LIMITEDToo many requests per minute. Retry after the Retry-After header.
429KEY_QUOTA_EXCEEDEDDaily quota used up. Resets at 00:00 UTC.
429FASTNEAR_RATE_LIMITEDThe service is busy. Retry in a minute.
200UNSUPPORTED_TOKENThe account is not an NEP-141 token (job status failed).
200UNSUPPORTED_ZERO_SUPPLYThe token reports a total supply of zero (job status failed).

Troubleshooting

SymptomFix
“This API key is not allowed on this website.”The page's host is not on your key's list, or the browser hid it (see the Referrer-Policy note above).
“This API key is not valid.”Typo, or the key was rotated.
“This API key is paused / revoked / expired.”Contact us.
Empty frame, blocked by the browserYour CSP lacks frame-src https://nearbubbles.xyz.
“The usage limit of this integration has been reached.”Daily quota; it resets at 00:00 UTC.
The map shows no connectionsThe holders did not send tokens to each other: the map is clean. A notice at the bottom says so.

Terms of use

  • Keep the Powered by Nearbubbles badge visible. Do not hide it with CSS, cover it, or crop the iframe so it is cut off.
  • Do not call the embed's internal endpoints directly or share your key; use the iframe, or the server API if your key has it.
  • We can pause or revoke keys that are abused.
Need a key?

For integration access contact @Hector885 on telegram.