Platform
Platform
How the X-Ray product works from a user's point of view — dashboards, filters, watchlists, and the live data feed. Pick a section in the sidebar to get started.
X-RAY developer docs: REST API reference, MCP integration for Claude, and guides for querying live Web3 labor-market data.
How the X-Ray product works from a user's point of view — dashboards, filters, watchlists, and the live data feed. Pick a section in the sidebar to get started.
A Watchlist Dashboard is your own private dashboard scoped to a list of companies you care about — your direct competitors, your portfolio, a prospect list, or anything else.
Every chart, KPI, and table that exists on the Global Dashboard re-runs against just that list, so you cut out the rest of the market and look only at what matters to you.
You can create as many watchlists as you like. Each one keeps its own benchmark settings and its own role filters, so a "L1 incumbents" dashboard and a "Q4 prospects" dashboard never get in each other's way.
The X-Ray REST API gives paid plans programmatic access to the same dataset that powers the dashboard — vacancies, companies, skills, and top-line stats.
The API is strictly read-only. There is no endpoint to create, update, or delete anything, and there never will be.
---
https://x-ray.me/api
Every call is a GET request that picks a resource via the type query parameter:
?type=vacancies | companies | skills | stats
Every request must carry a Bearer API key issued from Settings → API Keys in your dashboard.
Authorization: Bearer YOUR_API_KEY
Keys are shared across your workspace and require an active paid subscription or a standalone API plan. They're revoked automatically when a workspace downgrades.
Only GET is accepted. Any other HTTP verb returns 405 Method Not Allowed.
Successful responses:
{ "success": true, "data": [ /* rows */ ], "total": 1234, "limit": 100, "offset": 0 }
Errors:
{ "success": false, "error": "Human-readable message" }
| Status | Meaning | |---|---| | 200 | OK | | 400 | Invalid type parameter | | 401 | Missing or invalid API key | | 403 | No active paid / API subscription | | 405 | Non-GET method | | 500 | Internal error (a generic message; details are logged on our side) |
List endpoints accept limit (default 100, max 1000) and offset (default 0). The total field in the response is the unfiltered row count for your filter set.
There's no hard per-request rate limit. The fair-use ceiling is about 60 requests per minute per key. Sustained abuse will result in a key being revoked. See Rate Limits for tier-specific numbers.
From zero to your first API call in under five minutes.
local-dev), and copy the secret. The full key is shown once — store it somewhere safe.
export API_KEY="xrk_live_…"
export BASE="https://x-ray.me/api"
curl -G "$BASE?type=stats" \
-H "Authorization: Bearer $API_KEY"
A 200 with success: true confirms your key is active.
curl -G "$BASE" \
-H "Authorization: Bearer $API_KEY" \
--data-urlencode "type=vacancies" \
--data-urlencode "status=active" \
--data-urlencode "limit=100"
Walk pages by increasing offset until you reach total.
A complete mirror of every active role plus its company is around 5 MB and refreshes hourly:
let offset = 0;
const all = [];
for (;;) {
const r = await fetch(`${BASE}?type=vacancies&limit=1000&offset=${offset}`, {
headers: { Authorization: `Bearer ${API_KEY}` }
}).then(r => r.json());
all.push(...r.data);
if (r.data.length < 1000) break;
offset += 1000;
}
The API is read-only. There is no endpoint to create, update, or delete anything. Any non-GET request returns 405.
Every API request needs a Bearer token in the Authorization header:
Authorization: Bearer YOUR_API_KEY
| Key type | Where it comes from | Scope | |---|---|---| | User API key | Generated by a member of your workspace under Settings → API Keys | Shared across the workspace; read-only; auto-deactivated if the workspace's plan stops covering API access | | Admin key | Provisioned by X-Ray for partner integrations | Same read-only surface; no subscription check |
A user-issued key only works if the workspace has either:
If neither holds, the API returns 403 with one of:
"No active subscription""API access requires an active paid subscription or API plan""Subscription expired"Create a new key, deploy it everywhere, then delete the old key from the dashboard. There's no grace period — deleting a key takes effect immediately.
There's no hard per-request rate limit today. The fair-use ceilings are:
| Tier | Suggested ceiling | |---|---| | Paid workspace | 60 requests/min · 5,000 requests/day | | API plan add-on | 120 requests/min · 20,000 requests/day | | Admin key (partners) | Uncapped |
Sustained traffic above these ceilings can result in a key being revoked. If you need more throughput, talk to us through the Connect page — we have bulk export options for legitimate use cases.
All list endpoints (type=vacancies, type=companies, type=skills) accept:
| Parameter | Default | Max | |---|---|---| | limit | 100 | 1000 | | offset | 0 | — |
Pages are stable while you're walking them as long as the filters don't change. To mirror the full dataset, page with limit=1000 and increment offset until data.length < limit.
| Status | Body | Why | |---|---|---| | 200 | { success: true, data, total, limit, offset } | Success | | 400 | { success: false, error: "Invalid type…" } | type is missing or not one of vacancies / companies / skills / stats | | 401 | { success: false, error: "Missing API key" } | No Authorization header | | 401 | { success: false, error: "Invalid or inactive API key" } | Key not recognised or revoked | | 403 | { success: false, error: "No active subscription" } | Workspace has no paid plan and no API plan | | 403 | { success: false, error: "API access requires an active paid subscription or API plan" } | Workspace tier doesn't cover the API | | 403 | { success: false, error: "Subscription expired" } | Paid subscription has lapsed | | 405 | { success: false, error: "Method not allowed" } | Any non-GET verb | | 500 | { success: false, error: "Internal server error" } | Unhandled error on our side (details are logged) |
All responses are Content-Type: application/json with permissive CORS — the API key, not the origin, is the trust boundary.
GET /api?type=vacancies
Returns vacancies joined to their company (id, name, logo), newest first.
| Parameter | Type | Description | |---|---|---| | limit | int (≤1000) | Page size, default 100 | | offset | int | Pagination offset, default 0 | | status | string | active or closed | | category | string | Role category (e.g. Engineering, Design) | | region | string | Continental region | | country | string | Country name | | company_id | string | Restrict to a single company | | role_grade | string | Junior / Mid / Senior / Lead / Principal | | date_from | ISO date | Earliest posting date (inclusive) | | date_to | ISO date | Latest posting date (inclusive) |
curl -G "$BASE" \
-H "Authorization: Bearer $API_KEY" \
--data-urlencode "type=vacancies" \
--data-urlencode "status=active" \
--data-urlencode "role_grade=Senior" \
--data-urlencode "limit=50"
{
"success": true,
"data": [
{
"id": "…",
"title": "Senior Solidity Engineer",
"category": "Engineering",
"role_grade": "Senior",
"country": "Germany",
"region": "Europe",
"location": "Berlin",
"salary_min": 120000,
"salary_max": 180000,
"employee_count": 85,
"status": "active",
"date_added": "2026-05-12T10:14:00Z",
"date_removed": null,
"company": { "id": "…", "name": "Aave", "logo": "https://…/aave.png" }
}
],
"total": 18420,
"limit": 50,
"offset": 0
}
The role description is intentionally not returned in list responses to keep payloads small. Fetch the full description through the dashboard UI where it's sanitised.
X-Ray is a market intelligence terminal for Web3 hiring. It tells you who is hiring, what they pay, which skills are in demand, and where companies are growing or going quiet — based on real job postings from across the industry.
These docs walk you through what the product can do and how to use it.
---
---
> [!TIP] > Brand new? Skim Global Dashboard → TL;DR first to understand what you're looking at, then dive into whichever section matches your use case.
---
Use the Get in Touch form in your dashboard. A real person reads every message and we usually reply within a business day.
The Global Dashboard shows the entire Web3 hiring market. A Watchlist Dashboard shows only the companies you picked.
| | Global Dashboard | Watchlist Dashboard | |---|---|---| | Companies shown | Everyone we track | The ones you added | | Filters available | Sector, Sub-sector, Location, Blockchain, plus role-side filters | Role-side only: Role Category, Role Grade, Job Location | | Tabs | Overview, Hiring Trends, Sectors, Compensation, Skills & Tech, Geography, Companies, Vacancies | Overview, Activity, Compensation, Skills, Roles, Compare | | Benchmark control | n/a | Per-company peer pool, by sector | | Saved? | Stateless — every visit is fresh | Saved to your account, visible to your workspace |
Everything else — time-range chips (7d / 30d / 90d / All / Custom / Compare), the meta line in the header, embed buttons — works the same way in both places.
The Global Dashboard is the whole Web3 hiring market in one place — every company we track, every role they've posted, every salary, every skill.
This section walks through what each tab shows you, how the filters work, what the KPIs really mean, and how to read Compare mode.
---
If you're new, walk these in order. If you're coming back for a specific number, jump straight to the page you need.
| Page | What it covers | |---|---| | TL;DR | The 60-second version. | | Data Pipeline | Where the data comes from and how fresh it is. | | Time Range Controls & Meta Line | The chips and the dataset info shown in the header. | | Filter Model | The two filter groups and how they combine. | | Core Definitions | Active vs closed vs added — what each one means. | | Tabs and Charts | What each chart on each tab is showing. | | Compare Mode | How to read primary vs compare. | | Determinism, Caching, Stability | Why numbers stay stable across refreshes. | | Embedded Charts | Embedding a chart on your own site. | | What is Intentionally Not Here | Things we will not add, and why. | | Reproducing a Number | How to double-check any KPI on the screen. | | Glossary | Plain-language definitions of every term. |
The Global Dashboard takes every job posting across the companies we track and turns it into trends, KPIs, and breakdowns — by sector, by skill, by location, by company.
No AI-written summaries. No "market sentiment" narratives. No predictions about the future. The dashboard shows you what happened — interpretation is up to you.
> [!IMPORTANT] > Two people looking at the same dashboard with the same filters should always see identical numbers. If you ever see something that looks off, refresh the page first (it bypasses the cache), then tell us through the Get in Touch form.