AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Reverse Api

skill-metterian-reverse-api-skill-reverse-api-skill · by metterian

Use when reverse-engineering a web app's internal API from network requests, building an unofficial API client, or capturing and analyzing HTTP traffic to create programmatic access to a website without an official API

No reviews yet
0 installs
27 views
0.0% view→install

Install

$ agentstack add skill-metterian-reverse-api-skill-reverse-api-skill

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-metterian-reverse-api-skill-reverse-api-skill)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Reverse Api? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Reverse-Engineer Web App Internal API

Overview

Analyze captured network requests from any web app and generate a Python httpx client with API reference documentation. Works with REST, GraphQL, Google batchexecute, gRPC-web, and other protocols.

Core principle: Capture real requests, understand the protocol, generate code that mimics the browser exactly.

When to Use

  • Building programmatic access to a site with no official API
  • Reverse-engineering internal API endpoints from browser network traffic
  • Creating an unofficial Python client for a web app
  • Analyzing captured HTTP requests to understand API patterns

Invocation

/reverse-api                          # Init: scaffold project + analyze first endpoint
/reverse-api add "list applications"  # Add: new method to existing client

Mode Detection

digraph mode {
    "Client file exists?" [shape=diamond];
    "Chrome DevTools MCP available?" [shape=diamond];
    "Init Mode\n(scaffold + first endpoint)" [shape=box];
    "Add Mode\n(append method to client)" [shape=box];
    "Auto Capture\n(navigate + get_network_request)" [shape=box];
    "Paste Mode\n(user provides request data)" [shape=box];

    "Client file exists?" -> "Init Mode\n(scaffold + first endpoint)" [label="no"];
    "Client file exists?" -> "Add Mode\n(append method to client)" [label="yes"];
    "Init Mode\n(scaffold + first endpoint)" -> "Chrome DevTools MCP available?";
    "Add Mode\n(append method to client)" -> "Chrome DevTools MCP available?";
    "Chrome DevTools MCP available?" -> "Auto Capture\n(navigate + get_network_request)" [label="yes"];
    "Chrome DevTools MCP available?" -> "Paste Mode\n(user provides request data)" [label="no"];
}

How to detect Chrome DevTools MCP: Check if navigate_page and get_network_request tools are available in the current session.

Init Mode

When no client file exists in the project, run initialization:

Step 1: Gather site info

Ask the user:

  1. Target URL — the web app URL (e.g., https://app.greenhouse.io)
  2. Site name — used for naming (e.g., greenhouse)

Step 2: Scaffold project structure

Create:

src/_client/
├── __init__.py       # Package with version
├── client.py         # BaseClient with auth + HTTP infra
├── constants.py      # Endpoint URLs, code mappings
└── types.py          # Response dataclasses
docs/
└── api_reference.md  # Discovered API documentation

BaseClient template — generate client.py with:

import httpx

class BaseClient:
    """Client for  internal API."""

    BASE_URL = ""

    def __init__(self, cookies: dict[str, str], **auth_kwargs):
        self.cookies = cookies
        self._client: httpx.Client | None = None
        # Store additional auth params (csrf_token, api_key, etc.)
        self._auth = auth_kwargs

    def _get_client(self) -> httpx.Client:
        if self._client is None:
            self._client = httpx.Client(
                cookies={k: v for k, v in self.cookies.items()},
                headers={
                    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
                    "Origin": self.BASE_URL,
                    "Referer": f"{self.BASE_URL}/",
                },
                timeout=30.0,
            )
        return self._client

    def close(self):
        if self._client:
            self._client.close()
            self._client = None

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

api_reference.md template:

#  Internal API Reference

Auto-generated by reverse-api skill. Each endpoint documented as discovered.

---

Step 3: Proceed to Request Capture

After scaffolding, immediately move to capturing the first endpoint.

Request Capture

Auto Capture (Chrome DevTools MCP)

1. navigate_page(url="")
2. Tell user: "Now perform the action you want to capture (e.g., click 'List Applications')"
3. Wait for user confirmation
4. get_network_request() — capture the relevant XHR/fetch request
5. Filter: ignore static resources (.js, .css, images, fonts)
6. Extract: URL, method, headers, body, response

Paste Mode (Manual)

Tell the user:

> Open Chrome DevTools (F12) → Network tab → perform the action → right-click the API request → Copy as cURL. > > Or copy these individually: > 1. Request URL > 2. Request Method > 3. Request Headers (especially Cookie, Content-Type, Authorization, X-CSRF-Token) > 4. Request Body (if POST/PUT/PATCH) > 5. Response Body (first 2000 chars is enough)

Parse the pasted data. If user pastes a cURL command, extract all fields from it.

Protocol Analysis

After capturing a request, analyze it to determine the protocol and patterns.

Protocol Detection

| Signal | Protocol | |--------|----------| | f.req= in body, URL contains batchexecute | Google batchexecute | | /graphql in URL, or body has query + variables fields | GraphQL | | REST verbs on resource paths (GET /api/v1/users/123) | REST | | content-type: application/grpc-web+proto | gRPC-web | | content-type: application/x-www-form-urlencoded with custom structure | Form-encoded RPC | | WebSocket upgrade in headers | WebSocket |

Auth Detection

| Signal | Auth Type | |--------|-----------| | Cookie: header with session tokens | Cookie-based | | Authorization: Bearer | Bearer token | | X-CSRF-Token or X-CSRFToken header | CSRF token (usually with cookies) | | at= parameter in body | Google-style CSRF | | X-API-Key or api_key param | API key | | No auth headers, public endpoint | None / Public |

Response Format Detection

| Signal | Format | |--------|--------| | Starts with )]}'\n | XSSI-prefixed JSON (Google) | | Valid JSON directly | Standard JSON | | Chunked with byte counts between JSON arrays | Google batchexecute response | | data: prefixed lines | Server-Sent Events (SSE) | | Binary content | Protobuf / gRPC |

What to Extract

For each captured request, document:

  1. Endpoint — URL pattern (replace IDs with {id} placeholders)
  2. Method — HTTP method
  3. Auth — which auth mechanism
  4. Request params — body structure with field names and types
  5. Response structure — field names, types, nesting
  6. Pagination — if applicable (cursor, offset, page)
  7. Rate limiting — response headers indicating limits

Code Generation

For Each Endpoint, Generate:

1. Method in client.py:

def list_applications(self, page: int = 1, per_page: int = 50) -> list[Application]:
    """List all applications.

    Discovered: 2026-04-02
    Endpoint: GET /api/v1/applications
    """
    client = self._get_client()
    response = client.get(
        f"{self.BASE_URL}/api/v1/applications",
        params={"page": page, "per_page": per_page},
    )
    response.raise_for_status()
    data = response.json()
    return [Application(**item) for item in data["results"]]

2. Dataclass in types.py:

from dataclasses import dataclass

@dataclass
class Application:
    id: str
    candidate_name: str
    status: str
    # ... fields discovered from response

3. Constants in constants.py (if applicable):

# Endpoint paths
APPLICATIONS_LIST = "/api/v1/applications"

# Status code mappings
STATUS_ACTIVE = 1
STATUS_ARCHIVED = 2

4. Entry in docs/api_reference.md:

## list_applications
- **URL:** `GET /api/v1/applications`
- **Auth:** Cookie + CSRF
- **Parameters:**
  | Param | Type | Required | Description |
  |-------|------|----------|-------------|
  | page | int | no | Page number (default: 1) |
  | per_page | int | no | Results per page (default: 50) |
- **Response:**
  ```json
  {
    "results": [{"id": "...", "candidate_name": "...", "status": "..."}],
    "total": 150,
    "page": 1
  }
  ```
- **Discovered:** 2026-04-02

Protocol-Specific Code Patterns

Google batchexecute:

def _call_rpc(self, rpc_id: str, params: Any) -> Any:
    """Execute a batchexecute RPC call."""
    params_json = json.dumps(params, separators=(',', ':'))
    f_req = [[[rpc_id, params_json, None, "generic"]]]
    body = f"f.req={urllib.parse.quote(json.dumps(f_req))}"
    if self._auth.get("csrf_token"):
        body += f"&at={urllib.parse.quote(self._auth['csrf_token'])}"
    response = self._get_client().post(self.BATCHEXECUTE_URL, content=body)
    # Parse XSSI-prefixed response
    text = response.text
    if text.startswith(")]}'"):
        text = text[4:]
    return json.loads(text.strip().split('\n')[1])

GraphQL:

def _query(self, query: str, variables: dict | None = None) -> Any:
    """Execute a GraphQL query."""
    payload = {"query": query}
    if variables:
        payload["variables"] = variables
    response = self._get_client().post(
        f"{self.BASE_URL}/graphql",
        json=payload,
    )
    response.raise_for_status()
    data = response.json()
    if "errors" in data:
        raise APIError(data["errors"])
    return data["data"]

REST: Use standard httpx methods directly (get, post, put, delete).

Add Mode

When client file already exists:

  1. Read existing code — understand current patterns, naming conventions, auth setup
  2. Capture new request — same as Request Capture section
  3. Analyze — same as Protocol Analysis section
  4. Generate method — follow existing code style exactly:
  • Same naming convention (snake_case, same prefix patterns)
  • Same error handling pattern
  • Same return type style (dataclass, dict, etc.)
  • Add to existing client class, not a new one
  1. Update docs — append to existing api_reference.md
  2. Update types/constants — add new dataclasses and constants as needed

Do NOT:

  • Change existing methods
  • Refactor the client structure
  • Change auth patterns
  • Add new dependencies unless absolutely necessary

Common Mistakes

| Mistake | Fix | |---------|-----| | Hardcoding session-specific IDs in client code | Use parameters — extract IDs from URLs into method arguments | | Ignoring response cookie updates | Some sites rotate cookies per request — update cookie jar | | Missing CSRF token refresh | CSRF tokens expire — add refresh logic if site uses them | | Not URL-encoding body params | Use urllib.parse.quote() for form-encoded bodies | | Assuming JSON response | Check Content-Type and response prefix (XSSI, SSE, etc.) | | Copying all headers from capture | Only include required headers — User-Agent, Origin, Referer, Auth | | Not handling pagination | Always check if response indicates more pages |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.