Pricing Docs Contact Status Blog
Log in Get Free API Key →

JavaScript SDK

A zero-dependency JavaScript client for the IPFly API — caching, retries, rate limiting, request de-duplication and batch lookups, built in.

Get started → API Reference

Overview

The IPFly JavaScript SDK wraps the IPFly API in a single dependency-free file. It works as a browser global, a CommonJS require, or an ES module import, and it handles the operational details — caching, retries, rate limiting, and request de-duplication — so your integration code can stay focused on what to do with the response.

TTL + LRU caching

Response cache backed by memory, localStorage, or sessionStorage — configurable TTL and max size.

Automatic retries

Exponential backoff with jitter on network errors and 5xx responses. 4xx fails fast, no wasted retries.

Request de-duplication

Two calls for the same IP in the same tick share a single network request.

Batch lookups

Look up many IPs with bounded concurrency; a bad IP never fails the whole batch.

Client-side rate limiting

A token-bucket limiter smooths bursts so you don't blow through your plan's requests/second.

Zero dependencies

One UMD file — browser global, CommonJS, or ES module. MIT licensed.

Installation

No npm package is published — host ipfly-sdk.js yourself, alongside your other static assets or on your own CDN, and reference it by URL.

html — no build step
<script src="https://ipfly.world/libs/javascript-sdk/1.0.0.js"></script>
<script>
  const ipfly = IPFly.createClient({ token: 'YOUR_TOKEN' });
</script>
javascript — Node / bundlers
const IPFly = require('./1.0.0.js');
const ipfly = IPFly.createClient({ token: 'YOUR_TOKEN' });
javascript — ES modules
import IPFly from './1.0.0.js';
const ipfly = IPFly.createClient({ token: 'YOUR_TOKEN' });

Quick Start

Create a client once, then call lookup() wherever you need geolocation data. Every method returns a Promise.

javascript
const ipfly = IPFly.createClient({
  token: 'YOUR_TOKEN',
  include: 'security' // optional: request the security/ASN field set on every call
});

// Look up a specific IP
ipfly.lookup('8.8.8.8').then((data) => {
  console.log(data.city, data.country_name, data.security.is_vpn);
});

// Look up the caller's own IP (omit the ip argument)
ipfly.lookupSelf().then((data) => {
  console.log('You appear to be in', data.city);
});

// async/await works the same way
async function whoIsThis(ip) {
  try {
    return await ipfly.lookup(ip);
  } catch (err) {
    console.error(err.code, err.message);
  }
}

Security Note

Any token embedded in client-side JavaScript is visible to anyone who opens dev tools or views page source. This SDK runs in the browser because that's what makes it useful for drop-on-your-site use cases, but for a production public website you have two safe options.

1. Proxy it (recommended)

Run a thin backend endpoint that holds the real token, and point the SDK's baseURL at your own endpoint instead of https://ipfly.world/api. Your backend forwards the request to IPFly and returns the result — the token never reaches the browser.

2. Restrict the token's origin

In your IPFly dashboard, lock the token to your site's specific domain(s). A copied or stolen token then simply won't work from anywhere else. Treat this as a mitigation, not a substitute for proxying.

Prototyping? Calling IPFly directly from the browser with a domain-restricted token is a reasonable tradeoff. Shipping to production with meaningful traffic? Proxy it. See Examples below for a proxy pattern.

Client Options

All options are passed to IPFly.createClient({ ... }).

OptionTypeDefaultDescription
tokenstringrequiredYour IPFly API token.
baseURLstringipfly.world/apiOverride to point at your own backend proxy.
includestringnullDefault include value sent with every request (e.g. "security"). Overridable per call.
timeoutnumber8000Per-request timeout in ms before the request is aborted.
retriesnumber2Retry attempts for network errors and 5xx. 4xx is never retried.
retryDelaynumber300Base delay (ms) for exponential backoff; jitter is added automatically.
rateLimitnumber0Max requests/second this client will issue. 0 = unlimited.
concurrencynumber6Default max parallel requests for batchLookup.
cache.enabledbooleantrueSet false to disable caching entirely.
cache.ttlnumber300000How long a cached response stays valid, in ms (5 minutes).
cache.maxSizenumber500Max cached entries before least-recently-used entries are evicted.
cache.storagestring'memory''memory', 'localStorage', or 'sessionStorage'. Web Storage persists across page loads.
fetchfunctionglobal fetchSupply your own implementation (e.g. node-fetch) for non-browser environments.
debugbooleanfalseLog internal lifecycle events to the console (tokens are redacted).
onRequestfunctionnull(ip, url) => void — fired right before a network request is sent.
onResponsefunctionnull(ip, data, fromCache) => void — fired after a successful lookup.
onErrorfunctionnull(ip, error) => void — fired when a lookup ultimately fails, after retries.
onCacheHitfunctionnull(ip, data) => void — fired when a cached response is served instead of a network call.

.lookup() & .lookupSelf()

client.lookup(ip?, options?) looks up a single IP. Omit ip (or pass null/undefined) to geolocate the caller — or use the client.lookupSelf(options?) shorthand.

javascript
client.lookup('1.1.1.1', {
  include: 'security',            // overrides the client-level `include` for this call only
  skipCache: true,                 // force a fresh network request, bypassing the cache
  signal: abortController.signal    // optional AbortSignal to cancel this specific call
});

Returns a Promise that resolves with the parsed JSON response (see Response Shape) or rejects with an IPFlyError (see Error Handling).

.batchLookup()

client.batchLookup(ips, options?) looks up an array of IPs with bounded concurrency (options.concurrency overrides the client default). This call never rejects — each entry in the resolved array is a success or failure record, so one bad IP doesn't stop the batch.

javascript
const results = await client.batchLookup(['8.8.8.8', '1.1.1.1', 'not-an-ip']);
// [
//   { ip: '8.8.8.8',    ok: true,  data: { ... } },
//   { ip: '1.1.1.1',    ok: true,  data: { ... } },
//   { ip: 'not-an-ip',  ok: false, error: IPFlyError }
// ]

Cache Controls

MethodReturnsDescription
client.clearCache()voidEmpties the response cache immediately.
client.cacheStats()objectReturns { enabled, size, ttl, maxSize } — useful for a debug or admin/status panel.
client.destroy()voidClears the cache and any in-flight request bookkeeping. Call this when tearing down a client instance (e.g. on a route change in a single-page app).

IPFly.isValidIP()

A standalone helper that validates an IPv4 or IPv6 string without making a request.

javascript
IPFly.isValidIP('8.8.8.8');              // true
IPFly.isValidIP('2001:4860:4860::8888'); // true
IPFly.isValidIP('not-an-ip');            // false

Response Shape

Successful lookups resolve with the same JSON structure the IPFly API returns, unchanged — see the full field reference in the API docs. Shape depends on your plan and the include value requested.

json
{
  "ip": "8.8.8.8",
  "hostname": "dns.google",
  "country_name": "United States",
  "city": "Mountain View",
  "latitude": "37.4056",
  "longitude": "-122.0775",
  "time_zone": { "name": "America/New_York", "...": "..." },
  "asn": { "asn": "AS15169", "name": "Google LLC", "...": "..." },
  "security": { "is_vpn": false, "is_tor": false, "...": "..." }
}

Error Handling

All failures reject with an IPFlyError (a standard Error subclass) carrying message, status, code, and cause fields.

CodeMeaning
MISSING_TOKENNo token was supplied when creating the client.
NO_FETCHNo fetch implementation is available; supply one via config.fetch.
INVALID_IPThe IP string failed local validation before any request was sent.
TIMEOUTThe request exceeded timeout or was aborted.
NETWORK_ERRORThe request failed before a response was received (offline, DNS, CORS, etc).
HTTP_ERRORThe API responded with a non-2xx status — check status (401 = bad token, 403 = plan/permission issue, 404 = private/bogon IP, 429/5xx = retry-worthy).
BAD_RESPONSEThe server responded but the body wasn't valid JSON.
javascript
try {
  await client.lookup('8.8.8.8');
} catch (err) {
  if (err.code === 'TIMEOUT') {
    // retry later, show a "slow connection" message, etc.
  } else if (err.status === 401) {
    // token is invalid — surface a config error, don't retry
  }
  console.error(err.message);
}

Performance Notes

Caching

Avoids re-querying the same IP within the TTL window — handy for pages that look up the same visitor repeatedly, or dashboards re-rendering a list of known IPs.

De-duplication

If three components fire lookup('1.2.3.4') in the same tick, only one network request goes out; all three get the same result.

Rate limiting

A courtesy limiter on the client side that smooths bursts (e.g. rendering a table of 200 rows) so you don't blow through your plan's requests/second in a single frame.

Retries

Only apply to transient failures (network errors, timeouts, 5xx). A 401/403/404 fails fast since retrying won't fix a bad token or a private IP.

Examples

Two common integration patterns: rendering a small piece of personalized UI, and proxying instead of exposing your token.

html
<script src="https://ipfly.world/libs/javascript-sdk/1.0.0.js"></script>
<script>
  const ipfly = IPFly.createClient({
    token: 'YOUR_TOKEN',
    cache: { storage: 'sessionStorage', ttl: 30 * 60 * 1000 }
  });

  ipfly.lookupSelf()
    .then((data) => {
      document.getElementById('banner').textContent =
        `Welcome from ${'$'}{data.city || data.country_name}!`;
    })
    .catch(() => {
      document.getElementById('banner').textContent = 'Welcome!';
    });
</script>
javascript — browser
// Point at your own backend, no token here at all
const ipfly = IPFly.createClient({
  token: 'unused-placeholder', // your backend holds the real token
  baseURL: '/api/geo'          // your server-side route
});
javascript — Express (Node)
// Your backend, holding the real token server-side
app.get('/api/geo', async (req, res) => {
  const ip = req.query.ip || req.ip;
  const upstream = await fetch(
    `https://ipfly.world/api?token=${'$'}{process.env.IPFLY_TOKEN}&ip=${'$'}{ip}&include=security`
  );
  res.status(upstream.status).json(await upstream.json());
});

Browser Support

Any environment with Promise, Map, and fetch (or a supplied fetch polyfill/implementation) works — all evergreen browsers, and Node 18+ for server-side use. AbortController is used opportunistically for timeouts/cancellation and is optional: its absence just disables timeout support, it won't throw.

License

MIT — use it, fork it, ship it.

Build on reliable IP Intelligence

Start with free requests per day. No credit card. No commitments. Scale when you’re ready.