How to Detect C3.js on Any Website (API Guide)
C3.js is a reusable chart library: a friendly wrapper around D3 that lets a developer draw line, bar, and pie charts from a config object instead of hand-writing raw D3 selections. You will find it powering internal dashboards, WordPress analytics widgets, documentation sites, and a long tail of data-heavy admin panels that wanted D3-quality charts without the D3 learning curve. Because it always travels with D3, spotting C3.js on a page tells you a lot about how that site handles client-side data visualization.
This post covers two ways to detect C3.js: the manual browser-console method for a single site, and the DetectZeStack API for scanning thousands of domains. Every code example is copy-pasteable and uses fields that actually exist in the API response.
What Is C3.js and Why Detect It
C3.js (the "C3" is short for the library's original "comfortable, controllable, composable charts" pitch) renders SVG charts by generating D3 code for you from a declarative configuration. You hand it an array of data and a chart type, and C3 wires up the axes, tooltips, legends, and transitions. That trade — less flexibility than raw D3 in exchange for far less code — made it a popular default for teams that needed charts on a deadline.
Knowing whether a page uses C3.js is useful in several contexts:
- Competitive and product research — C3.js signals a custom, in-house dashboard rather than an embedded third-party BI tool. That distinction matters when you are sizing up how a competitor built their analytics surface.
- Sales prospecting — teams selling charting components, BI platforms, or data-visualization consulting want to find sites already invested in the D3 / C3.js ecosystem, because those buyers already value client-side visualization.
- Dependency and migration audits — C3.js reached its last release years ago and is effectively in maintenance mode. Teams that want to move to a maintained successor need a real inventory of every page that still loads it.
- Technology-trend research — measuring adoption of C3.js across a population of sites requires reproducible, automated data, not a screenshot of DevTools.
How C3.js Leaves Fingerprints in Page Source
C3.js is straightforward to detect because it announces itself in two independent ways, and its D3 dependency leaves its own mark.
First, when C3.js loads through a normal <script> tag it attaches a global c3 object to window, exposing c3.version and a c3.generate() factory. Second, the script tag itself usually points at a recognizable file, and it is almost always paired with the c3.css stylesheet that ships alongside it:
- cdnjs:
cdnjs.cloudflare.com/ajax/libs/c3/<version>/c3.min.js - jsDelivr or unpkg:
cdn.jsdelivr.net/npm/c3@<version>/c3.min.js - A path under the site itself:
/static/js/c3.min.jsor/wp-content/.../c3.min.js?ver=0.7.20
Because C3.js renders through D3, a page that loads it nearly always loads d3.min.js right before it. That dependency chain is itself a strong signal: see both C3.js and D3 and you are almost certainly looking at a C3-powered charting surface rather than an unrelated script.
Manual Ways to Detect C3.js in the Browser
For a one-off check on a single site, the browser is the fastest tool. There are two complementary approaches: inspecting the script tags, and probing the live JavaScript globals in the console.
Checking the DevTools Console and Page Source
Without running any JavaScript, a quick grep against the HTML answers the question for most sites. The library file is almost always named c3.js or c3.min.js:
curl -s https://example.com | grep -oE 'c3(\.min)?\.js' | head -3
To also surface the version when the script URL carries a ?ver= query parameter — the pattern WordPress uses when it enqueues the asset:
curl -s https://example.com | grep -oE 'c3(\.min)?\.js\?ver=[0-9.]+' | head -3
For the live check, open DevTools on the target page and run the following in the console. It probes the c3 global, reads the version, and checks for the D3 dependency that travels with it:
(function () {
var hasC3 = typeof window.c3 === 'object' && window.c3 !== null;
var version = hasC3 && window.c3.version ? window.c3.version : null;
var hasD3 = typeof window.d3 === 'object' && window.d3 !== null;
console.log({ c3: hasC3, version: version, d3: hasD3 });
})();
A dashboard built on the full stack prints something like:
{ c3: true, version: "0.7.20", d3: true }
C3.js is coupled to a specific era of D3. C3.js targets D3 v3 through v5, and the last C3 release predates the D3 v6+ module split. If you are auditing for a D3 upgrade, a C3.js hit is a flag that the page cannot simply move to modern D3 without swapping the charting layer too. Capture the c3.version string, do not just record "uses C3.js".
The console method is perfect for one URL, but it does not scale to 200 domains, let alone 200,000. For that you want an API.
Detect C3.js at Scale with the DetectZeStack API
The DetectZeStack /analyze endpoint returns every detected technology on a page as structured JSON, including C3.js and its D3 dependency when present. The detector reads the HTTP response — headers and HTML — so async loaders and noConflict-style tricks that fool a too-early console check are not a problem server-side.
Try It Without an API Key
The public /demo endpoint runs the same detector pipeline against any URL with no authentication. It is rate-limited but perfect for confirming the response shape before you wire up a real key:
curl -s "https://detectzestack.com/demo?url=https://c3js.org/" | python3 -m json.tool
The response is a single JSON object. Trimmed to the relevant entries, a C3.js hit looks like this:
{
"url": "https://c3js.org/",
"domain": "c3js.org",
"technologies": [
{
"name": "C3.js",
"categories": ["JavaScript libraries"],
"confidence": 100,
"description": "D3 based reusable chart library",
"website": "https://c3js.org/",
"icon": "C3.js.png",
"source": "http",
"version": "0.7.20",
"cpe": ""
},
{
"name": "D3",
"categories": ["JavaScript graphics"],
"confidence": 100,
"description": "D3.js is a JavaScript library for producing dynamic, interactive data visualisations in web browsers.",
"website": "https://d3js.org",
"icon": "D3.png",
"source": "http",
"version": "",
"cpe": ""
}
],
"categories": {
"JavaScript libraries": ["C3.js"],
"JavaScript graphics": ["D3"]
},
"meta": { "status_code": 200, "tech_count": 6, "scan_depth": "full" },
"cached": false,
"response_ms": 1712
}
Reading the technologies Array for C3.js and D3
Two things in that response are worth pointing out. First, C3.js carries a single category — JavaScript libraries — so it shows up under exactly that key in the top-level categories map. Second, D3 appears as its own entry, in the separate JavaScript graphics category, because D3 has an independent fingerprint. C3.js does not "imply" D3 automatically; D3 is reported because the page genuinely loads it. In practice that is exactly what you want: confirmation of the whole stack, not just the top layer.
Note the meta object holds only three fields — status_code, tech_count, and scan_depth. The timing (response_ms) and the cached flag are top-level fields, not nested inside meta. Read them from the root of the object.
API Example: One curl Call
For production usage, sign up on RapidAPI and call /analyze with your key. The endpoint returns the same shape as /demo but without the demo rate limit, and the response counts against your monthly quota:
curl -s "https://detectzestack.p.rapidapi.com/analyze?url=https://example.com" \
-H "x-rapidapi-key: $RAPIDAPI_KEY" \
-H "x-rapidapi-host: detectzestack.p.rapidapi.com" \
| jq '.technologies[] | select(.name == "C3.js")'
If C3.js is detected, that pipe prints exactly the matching object:
{
"name": "C3.js",
"categories": ["JavaScript libraries"],
"confidence": 100,
"description": "D3 based reusable chart library",
"website": "https://c3js.org/",
"icon": "C3.js.png",
"source": "http",
"version": "0.7.20",
"cpe": ""
}
If C3.js is not present, jq outputs nothing and the script exits cleanly — easy to wire into a shell pipeline that filters a list of domains. To confirm the whole charting stack in one pass, widen the filter to either name:
curl -s "https://detectzestack.p.rapidapi.com/analyze?url=https://example.com" \
-H "x-rapidapi-key: $RAPIDAPI_KEY" \
-H "x-rapidapi-host: detectzestack.p.rapidapi.com" \
| jq '[.technologies[] | select(.name == "C3.js" or .name == "D3") | .name]'
That returns an array such as ["C3.js", "D3"] when both are present, or just ["D3"] on a site that uses D3 directly without the C3.js layer — a useful distinction if you are hunting specifically for the C3 wrapper rather than raw D3.
Parsing the Version Field
The version field is a plain string when the detector found one ("0.7.20", "0.6.14") and empty when it did not. For C3.js the version signal comes from a ?ver= query parameter on the script URL, which WordPress and other asset pipelines add automatically. When C3.js is bundled into a single application file with webpack or Rollup, or served from a plain CDN path with no query string, the number is not visible from the HTTP layer, so version comes back empty while name still reports C3.js. For an audit that needs a real version inventory, treat an empty string as "present, version not detectable from this signal," not as "absent."
Batch Scanning Many Domains for C3.js
The whole point of an HTTP API is that "scan one site" and "scan ten thousand" are the same code with a different loop. A minimal worker that reads a domain list, calls /analyze, and writes one JSON line per domain:
#!/usr/bin/env bash
# scan-c3js.sh — usage: scan-c3js.sh domains.txt > results.jsonl
set -euo pipefail
KEY="${RAPIDAPI_KEY:?set RAPIDAPI_KEY}"
HOST="detectzestack.p.rapidapi.com"
while IFS= read -r domain; do
[ -z "$domain" ] && continue
curl -s "https://${HOST}/analyze?url=https://${domain}" \
-H "x-rapidapi-key: ${KEY}" \
-H "x-rapidapi-host: ${HOST}" \
| jq -c --arg d "$domain" '{
domain: $d,
c3js: (.technologies[]? | select(.name == "C3.js")) // null,
d3: ([.technologies[]? | select(.name == "D3")] | length > 0)
}'
done < "$1"
Each output line is either {"domain":"...","c3js":{...},"d3":true} when C3.js is present or {"domain":"...","c3js":null,"d3":false} when it is not. Pipe the result into a database, a spreadsheet, or another jq filter to produce the report you actually need — for example, every site that loads D3 but not C3.js, which is a useful prospecting list if you sell a charting layer, or every site still on a pre-0.7 C3 build, which is a useful migration target list.
For higher-throughput patterns (concurrency, retries, quota handling), see Batch Scan 1,000 Websites for Tech Stack.
Frequently Asked Questions
How do I check if a website uses C3.js?
For a single page, open DevTools and run typeof c3 in the console — if it returns "object", C3.js is loaded and c3.version gives you the release. Without JavaScript, grep the HTML for a c3.js or c3.min.js script tag. For many pages at once, call the DetectZeStack /analyze endpoint and filter technologies[] for name == "C3.js".
Does a C3.js hit mean the site also uses D3?
Almost always. C3.js is a wrapper over D3 and cannot render without it, so the /analyze response usually lists both as separate entries. If you see C3.js but no D3, the page is likely loading a non-standard or heavily bundled build — worth a closer manual look.
Is C3.js the same as Chart.js or D3?
No. D3 is the low-level visualization engine. C3.js is a thin declarative wrapper on top of D3. Chart.js is a separate, canvas-based library that does not use D3 at all. The DetectZeStack detector treats all three as distinct technologies, so you can tell exactly which charting approach a site chose.
Can the API detect C3.js behind a CDN or WordPress plugin?
Yes. Detection is based on the script URL and page markup that the origin returns, so as long as the c3.js or c3.min.js file is referenced in the delivered HTML — whether from cdnjs, jsDelivr, or a WordPress plugin's bundled assets — the detector reports it. The ?ver= query string that plugins add is also what feeds the version field.
Conclusion
Detecting C3.js on a single page takes one line in the DevTools console. Detecting it across a portfolio takes one HTTP call per domain. The two details that turn a yes/no answer into a real decision are the version field — because C3.js is a maintenance-mode library pinned to older D3 — and the presence of the D3 entry alongside it, which confirms you are looking at the full charting stack rather than an unrelated script that happens to be named c3.
The same /analyze call that surfaces C3.js also names every other framework, library, CDN, analytics tag, and CMS on the page. One quota, one response shape, every technology in one shot.
Related Reading
- How to Detect dc.js on Any Website — the dimensional-charting cousin of C3.js, also built on D3 and crossfilter
- Detect the JavaScript Framework on Any Website — React, Vue, Angular, Svelte, and the rest in one call
- Find Companies Using jsDelivr — C3.js and D3 are frequently delivered over jsDelivr; build a list of sites that load them that way
- How to Detect jQuery on Any Website — console tricks, CDN patterns, and version parsing for the web's most common library
- Batch Scan 1,000 Websites for Tech Stack — concurrency, retries, and quota patterns
Detect C3.js and Every Other Library in One API Call
One HTTP request returns every framework, library, CDN, CMS, and analytics tag on a page — with version strings where available. 100 requests per month free. No credit card.
Get your free API key