localapi
Turn any Python function into a local HTTP API with zero boilerplate.
from localapi import LocalAPI
app = LocalAPI()
@app.endpoint
def add(a: int, b: int) -> int:
return a + b
app.run()
curl -X POST localhost:8000/add \
-H "Content-Type: application/json" \
-d '{"a": 10, "b": 20}'
# → {"result": 30}
Interactive docs are at http://127.0.0.1:8000/docs.
Install
pip install -e . # from a checkout
pip install -e ".[test]" # with test dependencies
What you get from the signature
| Derived from | Becomes |
|---|---|
Function name add |
Route /add |
@app.endpoint / @app.get |
POST (JSON body) / GET (query string) |
| Parameters | Request fields; defaults make them optional |
| Type hints | Validation (unannotated parameters accept any JSON value) |
| Return hint | Response schema in OpenAPI |
| Docstring | Endpoint description in OpenAPI |
Usage
from localapi import LocalAPI
app = LocalAPI(title="My Tools", version="1.0")
@app.endpoint # POST /greet
def greet(name: str, excited: bool = False) -> str:
"""Say hello."""
return f"Hello {name}" + ("!" if excited else "")
@app.get # GET /status
def status() -> dict:
return {"status": "ok"}
@app.endpoint(path="/math/add", name="addition")
def add(a: int, b: int) -> int:
return a + b
app.run() # http://127.0.0.1:8000
Decorators return the original function, so it stays callable and testable as plain Python.
async def functions are awaited; regular functions run in a threadpool so slow ones don't block the server.
For one-file scripts there's a default instance:
from localapi import api
@api
def add(a: int, b: int):
return a + b
api.run()
Responses
Every response uses the same envelope:
// 200
{"result": 12}
// 422: invalid input
{"error": "validation_error", "detail": [{"field": "a", "message": "Input should be a valid integer"}]}
// 500: the function raised
{"error": "ValueError", "detail": "b must be positive"}
Return values can be primitives, dict/list, Pydantic models, dataclasses, datetime, Path (as a string), bytes (base64), enums, and anything FastAPI's jsonable_encoder understands.
Testing and other ASGI servers
app.asgi is the underlying FastAPI app:
from fastapi.testclient import TestClient
client = TestClient(app.asgi)
assert client.post("/add", json={"a": 1, "b": 2}).json() == {"result": 3}
Security
app.run() binds to 127.0.0.1. Passing another host= (for example 0.0.0.0) exposes your functions to the network and prints a warning. There's no authentication yet, so only do that on a trusted network.
Metadata
Release files for localapi 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| localapi-0.1.0.tar.gz | 16.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| localapi-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.9 kB
Release files / localapi-0.1.0.tar.gz
| Download URL | localapi-0.1.0.tar.gz |
|---|---|
| Size | 16.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
52946048a9be407433d81a3dd013c3e689badb3e92499767291935e32a0752df
|
|
BLAKE2b-256 checksum How to use checksums |
4574474f9e616326193c53dff962df9e8fe017f8bd77d70ba80dba1f5def83b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|
Release files / localapi-0.1.0-py3-none-any.whl
| Download URL | localapi-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a39791983b7d9d0bc0bb83e5a4e1743915b5ee7897a5035a72e4cbe1d74c0ee0
|
|
BLAKE2b-256 checksum How to use checksums |
dc437942bfbb56ec754ffe851536d20758d3e7e4a26dfb76a1ce56089248bb26
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|