Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions examples/apple/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,25 @@ print(json.dumps(results, indent=2, ensure_ascii=False))
```

Save this as `quick_example.py` and run it to get started immediately!

---

## Don't want to maintain this?

This client was reverse-engineered locally and is yours to keep — but it is
pinned to jobs.apple.com's API as it looked on the day of capture, and nothing here
re-engineers it when that changes.

[Anything](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=apple)
is the hosted version of this project: you describe the task and get back a
deployed API function, with the proxies, retries, and repair-on-change handled
for you. Check whether Apple's careers site is already covered before building anything:

```bash
reverse-api-engineer marketplace search --site jobs.apple.com
```

If nothing matches yet, describe the task at
[anything.notte.cc](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=apple)
and it gets built for you. Agents can search the same catalogue by pointing an
MCP client at `https://anything.notte.cc/mcp`.
22 changes: 22 additions & 0 deletions examples/ashby/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -700,3 +700,25 @@ Please document and share your findings.
## Contact

For questions about this reverse engineering project, refer to the HAR analysis source file.

---

## Don't want to maintain this?

This client was reverse-engineered locally and is yours to keep — but it is
pinned to jobs.ashbyhq.com's API as it looked on the day of capture, and nothing here
re-engineers it when that changes.

[Anything](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=ashby)
is the hosted version of this project: you describe the task and get back a
deployed API function, with the proxies, retries, and repair-on-change handled
for you. Check whether Ashby-hosted job boards is already covered before building anything:

```bash
reverse-api-engineer marketplace search --site jobs.ashbyhq.com
```

If nothing matches yet, describe the task at
[anything.notte.cc](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=ashby)
and it gets built for you. Agents can search the same catalogue by pointing an
MCP client at `https://anything.notte.cc/mcp`.
22 changes: 22 additions & 0 deletions examples/autoscout24/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,25 @@ This client was generated through automated reverse engineering. Improvements an
---

**Generated by Claude Code** | 2026-01-03

---

## Don't want to maintain this?

This client was reverse-engineered locally and is yours to keep — but it is
pinned to autoscout24.com's API as it looked on the day of capture, and nothing here
re-engineers it when that changes.

[Anything](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=autoscout24)
is the hosted version of this project: you describe the task and get back a
deployed API function, with the proxies, retries, and repair-on-change handled
for you. Check whether AutoScout24 listings is already covered before building anything:

```bash
reverse-api-engineer marketplace search --site autoscout24.com
```

If nothing matches yet, describe the task at
[anything.notte.cc](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=autoscout24)
and it gets built for you. Agents can search the same catalogue by pointing an
MCP client at `https://anything.notte.cc/mcp`.
109 changes: 109 additions & 0 deletions examples/hosted/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# The same job, three ways

Every other folder in `examples/` is the output of a local capture: a client
file you own, pinned to the site as it looked that day. This one is the
comparison — the same "get data out of a website" job done locally, done
hosted over HTTP, and done hosted from an agent.

Nothing here is a pitch for one of them. They fail differently, and which one
you want depends on who maintains the result.

## The three routes

| | Local (this repo) | Hosted over HTTP | Hosted over MCP |
|---|---|---|---|
| You get | a client file in your repo | a callable endpoint | a tool your agent can call |
| Setup | install the CLI, run a capture | an API key | one config line |
| Runs on | your machine | Anything's browsers | Anything's browsers |
| Site changes | you re-run the capture | re-engineered for you | re-engineered for you |
| Cost | your model tokens | per run | per run |
| Offline | yes | no | no |
| Code review | you read every line | you read the output | you read the output |

## 1. Local — reverse-engineer it yourself

```bash
reverse-api-engineer
> get NFL team standings for the current season
```

A browser opens, traffic is captured, and your model writes the client into
`./scripts/nfl_standings_api/`. See any of the sibling example folders for
what that output looks like in practice.

The client is MIT, yours, and runs anywhere. It also stops working the day
nfl.com changes its endpoints, and re-running the capture is on you.

## 2. Hosted over HTTP

First, find out whether the function already exists. The marketplace search
endpoint is public — no key, no account:

```bash
python discover.py nfl.com
```

```
5 function(s) for 'nfl.com':

get_nfl_team_standings [nfl.com] · 7 runs
Returns NFL team standings from https://nfl.com for a selected season,
season type, and week...
https://anything.notte.cc/marketplace/365309fa-acb6-4226-b410-9a5f86fb72d9
```

`discover.py` passes the site straight through as `base_url`, which scopes
the search server-side. Any form works — a bare hostname, a full URL, or a
glob like `*.nfl.*` — and subdomains are covered. Add a query alongside it to
narrow further: the two filters compose.

Then run one. This part needs a key from
[console.notte.cc](https://console.notte.cc):

```bash
export NOTTE_API_KEY=...
python run_hosted.py <function_id> '{"season": 2025}'
```

Each function declares its own variables; the names and types are on that
function's marketplace page, which `discover.py` prints for every result.

## 3. Hosted over MCP

Point an MCP client at the endpoint and your agent searches the same catalogue
on its own — checking for an existing function before it builds anything:

```json
{
"mcpServers": {
"anything": {
"url": "https://anything.notte.cc/mcp"
}
}
}
```

The server exposes `search` (public), plus `spec`, `run`, and `build` once
authenticated. `build` describes a task in plain English and deploys a new
function, which is the hosted equivalent of a capture run here.

## Which to use

Use the **local** route when the client belongs in your repo, when you need it
to run offline or inside your own network, when the site needs your logged-in
session, or when you want to read every line before it executes.

Use the **hosted** route when you want an endpoint rather than a file, when
you would rather not own the repair work each time the site shifts, or when an
agent needs to reach hundreds of sites without you building each one.

The [CLI ships with a marketplace lookup](../../README.md#hosted-version) for
exactly this reason: before a capture, it tells you if the site is already
covered, and enter carries on capturing anyway.

## Files

- `discover.py` — public marketplace search, runs with no credentials.
- `run_hosted.py` — execute one hosted function, needs `NOTTE_API_KEY`.

Both are dependency-free (standard library only) and target Python 3.11+.
75 changes: 75 additions & 0 deletions examples/hosted/discover.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Search the Anything marketplace for a function that already exists.

Runs with no API key and no account — the marketplace search endpoint is
public. Use it before spending a capture run on a site somebody has already
reverse-engineered.

python discover.py nfl.com
python discover.py "instagram post comments"
"""

import json
import sys
from urllib.parse import urlencode
from urllib.request import Request, urlopen

SEARCH_ENDPOINT = "https://anything.notte.cc/api/marketplace/search"
MARKETPLACE_URL = "https://anything.notte.cc/marketplace"


def search(query: str | None = None, base_url: str | None = None, limit: int = 5) -> list[dict]:
"""Return marketplace functions matching `query`, `base_url`, or both.

`base_url` scopes to one site and understands any form it is written in:
a bare hostname, a full URL, or a glob like `*.nfl.*`. `query` ranks
within whatever is left. The two compose.
"""
params = {"limit": limit}
if query:
params["q"] = query
if base_url:
params["base_url"] = base_url

url = f"{SEARCH_ENDPOINT}?{urlencode(params)}"
with urlopen(Request(url, headers={"User-Agent": "anything-example"}), timeout=15) as response:
payload = json.load(response)
return payload.get("results", [])


def functions_for_site(site: str, limit: int = 5) -> list[dict]:
"""Return the functions that belong to one site, most-run first."""
matches = sorted(
search(base_url=site, limit=limit),
key=lambda fn: fn.get("run_count", 0),
reverse=True,
)
return matches[:limit]


def main() -> int:
if len(sys.argv) < 2:
print(__doc__)
return 2

query = " ".join(sys.argv[1:])
# A bare hostname is almost always a "is this site covered?" question.
looks_like_domain = "." in query and " " not in query
results = functions_for_site(query) if looks_like_domain else search(query)

if not results:
print(f"No hosted function matches {query!r} yet.")
print("Describe the task at https://anything.notte.cc and it gets built for you.")
return 0

print(f"{len(results)} function(s) for {query!r}:\n")
for fn in results:
print(f" {fn['label']} [{fn.get('domain', '?')}] · {fn.get('run_count', 0)} runs")
if fn.get("description"):
print(f" {fn['description']}")
print(f" {MARKETPLACE_URL}/{fn['function_id']}")
print()
return 0


if __name__ == "__main__":
raise SystemExit(main())
74 changes: 74 additions & 0 deletions examples/hosted/run_hosted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Run a hosted Anything function instead of a locally generated client.

Unlike `discover.py`, this one needs a key: get one at https://console.notte.cc
and export it as NOTTE_API_KEY.

export NOTTE_API_KEY=...
python run_hosted.py 365309fa-acb6-4226-b410-9a5f86fb72d9 '{"season": 2025}'

Each function declares its own variables. The names and types are listed on
that function's marketplace page — `discover.py` prints the URL for every
result, so start there rather than guessing.
"""

import json
import os
import sys
from urllib.error import HTTPError
from urllib.request import Request, urlopen

RUN_ENDPOINT = "https://anything.notte.cc/api/functions/{function_id}/run"


def run(function_id: str, variables: dict, api_key: str) -> dict:
"""Execute one hosted function and return its result JSON.

The request mirrors the `run` tool exposed on the MCP endpoint: the
function id identifies which skill to execute, and `variables` carries the
values it declared.
"""
request = Request(
RUN_ENDPOINT.format(function_id=function_id),
data=json.dumps({"variables": variables}).encode(),
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"User-Agent": "anything-example",
},
method="POST",
)
# Hosted runs drive a real browser, so they are slow by design.
with urlopen(request, timeout=180) as response:
return json.load(response)


def main() -> int:
api_key = os.environ.get("NOTTE_API_KEY")
if not api_key:
print("error: set NOTTE_API_KEY (get one at https://console.notte.cc)", file=sys.stderr)
return 2

if len(sys.argv) < 2:
print(__doc__, file=sys.stderr)
return 2

function_id = sys.argv[1]
try:
variables = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {}
except json.JSONDecodeError as exc:
print(f"error: variables must be a JSON object ({exc})", file=sys.stderr)
return 2

try:
result = run(function_id, variables, api_key)
except HTTPError as exc:
body = exc.read().decode(errors="replace")
print(f"error: HTTP {exc.code} — {body}", file=sys.stderr)
return 1

print(json.dumps(result, indent=2, ensure_ascii=False))
return 0


if __name__ == "__main__":
raise SystemExit(main())
22 changes: 22 additions & 0 deletions examples/ikea/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,25 @@ This is an unofficial API client and is not affiliated with, endorsed by, or spo
---

**Generated with Claude Code** 🤖

---

## Don't want to maintain this?

This client was reverse-engineered locally and is yours to keep — but it is
pinned to ikea.com's API as it looked on the day of capture, and nothing here
re-engineers it when that changes.

[Anything](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=ikea)
is the hosted version of this project: you describe the task and get back a
deployed API function, with the proxies, retries, and repair-on-change handled
for you. Check whether IKEA's product catalogue is already covered before building anything:

```bash
reverse-api-engineer marketplace search --site ikea.com
```

If nothing matches yet, describe the task at
[anything.notte.cc](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=ikea)
and it gets built for you. Agents can search the same catalogue by pointing an
MCP client at `https://anything.notte.cc/mcp`.
22 changes: 22 additions & 0 deletions examples/mintlify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,25 @@ This client should work with any documentation site using Mintlify's AI assistan
## License

MIT License - Feel free to use and modify.

---

## Don't want to maintain this?

This client was reverse-engineered locally and is yours to keep — but it is
pinned to mintlify.com's API as it looked on the day of capture, and nothing here
re-engineers it when that changes.

[Anything](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=mintlify)
is the hosted version of this project: you describe the task and get back a
deployed API function, with the proxies, retries, and repair-on-change handled
for you. Check whether Mintlify-hosted docs sites is already covered before building anything:

```bash
reverse-api-engineer marketplace search --site mintlify.com
```

If nothing matches yet, describe the task at
[anything.notte.cc](https://anything.notte.cc?utm_source=rae&utm_medium=example&utm_campaign=mintlify)
and it gets built for you. Agents can search the same catalogue by pointing an
MCP client at `https://anything.notte.cc/mcp`.
Loading