api-discovery — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited api-discovery (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Modern web applications — especially Single Page Applications (SPAs) — communicate almost entirely through hidden APIs. These APIs are never documented publicly, yet they power every feature: product listings, search, real-time updates, user authentication, notifications, and payment flows.
This skill systematically discovers and maps these APIs, turning opaque frontends into transparent, analyzable systems.
Use Cases:
Why This Matters for Startups:
When you see a competitor's polished UI, you only see 20% of the product. The API layer reveals the other 80% — the data model, the business rules, the scalability approach, the edge cases they handle. This skill gives you X-ray vision into any web application.
discover_apis)Purpose: Intercept all XHR/fetch requests a website makes, revealing hidden API endpoints that power the frontend.
#### When imperium-crawl is available:
# Basic discovery — load page and capture all API calls
imperium-crawl discover-apis --url "target.com" --wait-seconds 10
# Extended discovery — navigate multiple pages to find more endpoints
imperium-crawl discover-apis --url "target.com" --wait-seconds 15 --navigate-links 5
# Deep discovery — interact with forms, buttons, modals to trigger API calls
imperium-crawl discover-apis --url "target.com" --wait-seconds 20 --interact --max-depth 3
# Discovery with authentication (if you have an account)
imperium-crawl discover-apis --url "target.com" --wait-seconds 15 --cookies "session=abc123"This loads the page in a headless browser, waits for API calls to fire, and returns all discovered endpoints with methods, headers, request/response bodies, and timing data.
#### When imperium-crawl is NOT available (manual approach):
Follow this systematic process:
Step 1: Prepare the Browser
Step 2: Passive Discovery — Initial Page Load
Step 3: Active Discovery — User Actions Trigger API calls by performing common user actions:
Step 4: Document Each API Call For every request captured, record:
#### What to Look For:
Base API URL patterns:
api.company.com — dedicated API subdomain (common in mature products)company.com/api/v2/ — path-based API (common in monoliths)company.com/graphql — GraphQL endpointcompany.com/_next/data/ — Next.js data routescompany.com/__api/ — framework-specific internal routesAuthentication detection:
Authorization: Bearer eyJ... — JWT token (decode at jwt.io for claims)Cookie: session=... — Cookie-based sessionX-API-Key: ... or ?api_key=... — API key authenticationAuthorization: Basic ... — Basic auth (rare in SPAs)/oauth/authorize redirectsX-CSRF-Token header or hidden form fieldsPagination patterns:
?cursor=abc123 or ?after=abc123 (modern, scalable)?offset=20&limit=10 (traditional, simple)?page=2&per_page=25 (traditional)Link: <url>; rel="next" (RFC 5988){"next_cursor": "abc", "has_more": true}Rate limiting detection:
X-RateLimit-Limit — max requests per windowX-RateLimit-Remaining — requests leftX-RateLimit-Reset — when window resetsRetry-After header on 429 responsesReal-time connections:
wss://ws.company.com/...text/event-stream content type/socket.io/?EIO=4&transport=pollingquery_api)Purpose: Test discovered endpoints to understand their behavior, data models, and business logic.
#### When imperium-crawl is available:
# Simple GET request
imperium-crawl query-api --url "api.target.com/v1/products" --method GET
# GET with query parameters
imperium-crawl query-api --url "api.target.com/v1/products?category=electronics&limit=5" --method GET
# POST with JSON body
imperium-crawl query-api --url "api.target.com/v1/search" --method POST \
--body '{"query": "test", "filters": {"price_min": 0}}'
# Request with custom headers
imperium-crawl query-api --url "api.target.com/v1/products" --method GET \
--headers '{"Accept": "application/json", "X-Requested-With": "XMLHttpRequest"}'
# GraphQL introspection query
imperium-crawl query-api --url "target.com/graphql" --method POST \
--body '{"query": "{__schema{types{name,fields{name,type{name}}}}}"}'#### When imperium-crawl is NOT available:
Use WebFetch MCP tool or browser DevTools console:
// From browser console (same-origin only)
fetch('/api/v1/products')
.then(r => r.json())
.then(data => console.log(JSON.stringify(data, null, 2)));Or use curl from terminal:
curl -s "https://api.target.com/v1/products" \
-H "Accept: application/json" | jq .Note: Without proper authentication tokens or cookies, most endpoints will return 401/403.
#### Analysis Checklist:
For each endpoint you query, analyze:
Access-Control-Allow-Origin: * means callable from any browser. Specific domain means restrictedCache-Control, ETag, Last-Modified reveal caching strategyv1, v2, v3 indicates maturity and iteration speedcus_...)monitor_websocket)Purpose: Observe real-time data streams to understand live features like notifications, chat, price updates, collaborative editing, and live dashboards.
#### When imperium-crawl is available:
# Monitor WebSocket for 30 seconds
imperium-crawl monitor-websocket --url "wss://ws.target.com/stream" --duration 30
# Monitor with initial subscription message
imperium-crawl monitor-websocket --url "wss://ws.target.com/stream" --duration 30 \
--send '{"type": "subscribe", "channel": "updates"}'
# Monitor Socket.IO connection
imperium-crawl monitor-websocket --url "wss://ws.target.com/socket.io/?EIO=4&transport=websocket" \
--duration 60#### When imperium-crawl is NOT available:
Browser DevTools approach:
What to analyze in WebSocket streams:
update, notification, ping, subscribe)Before running API discovery, determine if the target is a Single Page Application. SPAs are the richest targets for API discovery because they make all data requests through JavaScript.
1. View Page Source Check Right-click → View Page Source. If the HTML body is mostly empty with a single mount point, it's an SPA:
<!-- SPA indicator — nearly empty body -->
<body>
<div id="root"></div>
<script src="/static/js/main.abc123.js"></script>
</body>vs.
<!-- Server-rendered — content in HTML -->
<body>
<header>...</header>
<main>
<h1>Product Name</h1>
<div class="product-grid">...</div>
</main>
</body>2. Framework Detection Check browser console for framework globals:
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ or document.querySelector('[data-reactroot]')window.__VUE_DEVTOOLS_GLOBAL_HOOK__ or document.querySelector('[data-v-]')document.querySelector('[ng-version]') or window.ng__svelte attributes in DOMwindow.__NEXT_DATA__ (SSR + SPA hybrid — still rich API calls)window.__NUXT__3. Network Behavior SPAs have a distinctive network pattern:
4. URL Pattern
site.com/#/dashboard (older SPAs)site.com/dashboard but no full reload (modern SPAs)document type appears, it's an SPAModern sites are often hybrids (SSR + SPA):
__NEXT_DATA__ or similar hydration payloads in page source — these contain the initial API response dataStandard CRUD pattern:
GET /api/v1/resources → List (with pagination)
GET /api/v1/resources/:id → Get single resource
POST /api/v1/resources → Create new resource
PUT /api/v1/resources/:id → Full update (replace)
PATCH /api/v1/resources/:id → Partial update
DELETE /api/v1/resources/:id → DeleteCommon extensions:
GET /api/v1/resources/:id/children → Nested resources
POST /api/v1/resources/search → Complex search (body too large for GET query params)
POST /api/v1/resources/batch → Batch operations
GET /api/v1/resources/count → Count without fetching data
GET /api/v1/resources/export → CSV/PDF export
POST /api/v1/resources/import → Bulk importGraphQL uses a single endpoint for everything:
POST /graphql
# Query — read operations
{"query": "{ products(first: 10) { id name price } }"}
# Mutation — write operations
{"query": "mutation { createProduct(input: {name: \"Test\"}) { id } }"}
# Introspection — discover the entire schema
{"query": "{__schema{types{name,fields{name,type{name}}}}}"}
# Named queries with variables
{"query": "query GetProduct($id: ID!) { product(id: $id) { id name } }", "variables": {"id": "123"}}Detection clues:
/graphql or /api/graphql)query fielddata field (and optionally errors)Content-Type: application/json both waysBearer Token (JWT): Most common in SPAs. Token stored in localStorage or memory.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Decode the JWT payload at jwt.io to find: user ID, roles, expiration, issuer.
Cookie-Based Sessions: Traditional web apps. Cookie set by server, sent automatically by browser.
Cookie: session=abc123def456; _csrf=xyz789API Key: Common for public/developer APIs. Usually in header or query param.
X-API-Key: sk_live_abc123
?api_key=sk_live_abc123OAuth 2.0 Flows: Watch for redirect chains:
provider.com/oauth/authorize?client_id=...&redirect_uri=...app.com/callback?code=...For each target analyzed, generate a structured API intelligence report:
{
"target": "https://target.com",
"scan_date": "2025-01-15",
"is_spa": true,
"framework": "React (Next.js)",
"base_url": "https://api.target.com",
"api_version": "v2",
"auth_type": "bearer_jwt",
"auth_details": {
"token_location": "localStorage",
"token_key": "auth_token",
"jwt_algorithm": "RS256",
"token_expiry": "24h",
"refresh_mechanism": "POST /auth/refresh"
},
"total_endpoints": 15,
"endpoints": [
{
"method": "GET",
"path": "/v2/products",
"full_url": "https://api.target.com/v2/products",
"auth_required": true,
"rate_limited": true,
"rate_limit": "100/minute",
"response_format": "json",
"pagination": {
"type": "cursor",
"param": "after",
"default_page_size": 50
},
"query_params": ["category", "sort", "search", "after", "limit"],
"response_fields": ["id", "name", "price", "description", "images", "created_at"],
"notes": "Returns product catalog. Supports filtering by category and full-text search."
},
{
"method": "POST",
"path": "/v2/search",
"full_url": "https://api.target.com/v2/search",
"auth_required": false,
"rate_limited": true,
"rate_limit": "30/minute",
"response_format": "json",
"request_body": {
"query": "string",
"filters": "object",
"page": "number",
"size": "number"
},
"notes": "Full-text search with faceted filtering. Uses Elasticsearch under the hood (visible in error responses)."
}
],
"websockets": [
{
"url": "wss://ws.target.com/updates",
"purpose": "Real-time price and inventory updates",
"protocol": "custom JSON",
"message_format": "json",
"message_types": ["price_update", "stock_change", "heartbeat"],
"avg_frequency": "2 messages/second",
"auth_required": true,
"notes": "Requires auth token in initial connection as query param"
}
],
"graphql": {
"endpoint": "/graphql",
"introspection_enabled": true,
"schema_types": 45,
"notable_queries": ["products", "user", "orders", "recommendations"],
"notable_mutations": ["createOrder", "updateProfile", "addToCart"],
"notes": "Full introspection available. Schema suggests they use Shopify-style architecture."
},
"third_party_apis": [
{"service": "Stripe", "endpoint": "api.stripe.com", "purpose": "Payments"},
{"service": "Segment", "endpoint": "api.segment.io", "purpose": "Analytics"},
{"service": "Algolia", "endpoint": "*.algolia.net", "purpose": "Search"},
{"service": "Cloudinary", "endpoint": "res.cloudinary.com", "purpose": "Image CDN"}
],
"security_observations": {
"cors": "Restricted to target.com origins",
"csp": "Present and strict",
"hsts": true,
"api_key_exposure": "No API keys visible in frontend code",
"over_fetching": "User endpoint returns email and phone — potential PII concern"
},
"competitive_insights": [
"Uses cursor pagination — indicates large datasets and modern architecture",
"WebSocket for real-time pricing suggests dynamic pricing engine",
"GraphQL with 45 types — complex data model, heavy investment in API layer",
"Algolia for search instead of building in-house — focus on core product over infrastructure",
"Auth token expires every 24h with silent refresh — good security practice"
]
}STEP 1: SPA Detection
→ View source, check framework globals, observe network behavior
STEP 2: Passive Discovery (page load)
→ Record all API calls made on initial page load
→ Note base URLs, auth patterns, response formats
STEP 3: Active Discovery (interaction)
→ Trigger API calls through search, navigation, scrolling, modals
→ Look for CRUD endpoints, pagination, error patterns
STEP 4: WebSocket Discovery
→ Check for real-time connections (WS tab in DevTools)
→ Monitor message formats and frequency
STEP 5: GraphQL Introspection
→ If GraphQL detected, try introspection query
→ Map queries, mutations, and types
STEP 6: Analysis & Report
→ Structure findings into the output format above
→ Add competitive insights and strategic observations
→ Flag security concerns or over-exposed dataThis skill is designed for legitimate research and intelligence gathering only.
DO:
robots.txt and Terms of ServiceDO NOT:
Gray Areas:
When in doubt: If an endpoint clearly isn't meant to be public, don't use it. The goal is intelligence and learning, not exploitation.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.