Get started

Try an example

Optionally generate a runnable FastAPI app for your published revision before you change your own application.

This step is optional. recuut example writes a standalone FastAPI project for one published revision, so you can see a working paid endpoint before you protect your own route.

The command needs a published revision and a merchant test key from Set up your product.

Install the FastAPI extra in your Python environment and set the merchant key. The SDK uses https://api.recuut.com by default, so the usual path needs only RECUUT_API_KEY.

pip install "recuut[fastapi]>=0.11.0"
export RECUUT_API_KEY="recuut_test_..."

Generate the example for your published revision:

recuut example fastapi --revision rvk_... --output ./recuut-example

The command creates a new recuut-example directory containing app.py, a generated recuut_contracts package, pyproject.toml, and a README. It refuses to overwrite an existing path. If your revision belongs to a local, staging, or preview API, add --api-url https://your-api-origin to the command. The generated endpoint will use that same API origin.

From the generated directory, install its dependencies and start the server:

cd recuut-example && \  python -m pip install -e . && \  python -m uvicorn app:app --reload

Your generated app.py is similar to the following. Class names, input_map, and complete(...) arguments come from your revision.

app.py
"""Simulated FastAPI endpoint protected by one published Recuut revision."""from typing import Any, Selffrom fastapi import FastAPIfrom pydantic import BaseModel, Field, model_validatorfrom recuut.fastapi import recuutfrom recuut_contracts import TextGenerationV1, TextGenerationV1Paymentclass ExampleRequest(BaseModel):    prompt: str = Field(min_length=1, max_length=10_000)    # Usage is derived on the server; it is not request data.    simulated_input_tokens: int = Field(        default=0, exclude=True, ge=0, json_schema_extra={"readOnly": True}    )    @model_validator(mode="before")    @classmethod    def reject_client_usage(cls, value: Any) -> Any:        if isinstance(value, dict) and any(            name in value for name in ("simulated_input_tokens",)        ):            raise ValueError("Usage is set by the server.")        return value    @model_validator(mode="after")    def derive_usage(self) -> Self:        # Simulated value for each contract input.        self.simulated_input_tokens = 1        return selfclass ExampleResponse(BaseModel):    text: strapp = FastAPI(title="Recuut FastAPI example")@app.post("/example")@recuut(TextGenerationV1, input_map={"input_tokens": "payload.simulated_input_tokens"})def example(    payload: ExampleRequest,    payment: TextGenerationV1Payment,) -> ExampleResponse:    # These values are illustrative.    # Replace them with real server measurements.    return payment.complete(        ExampleResponse(text=payload.prompt.upper()),        output_tokens=1,    )

The example uppercases prompt and uses simulated value 1 for every contract input and reported value. The server sets those values; the request cannot set them. Replace the simulated handler and values with your application's real work and measurements before using the endpoint for real billing.

Once the server runs, send a request from another terminal:

curl --include \  --request POST \  --url http://127.0.0.1:8000/example \  --header 'Content-Type: application/json' \  --data '{"prompt":"hello world"}'

For a paid revision, expect HTTP 402 with PAYMENT-REQUIRED and no uppercase result. If it differs, check the published revision, active Price, Require payment setting, and test key.

Then test a paid request against http://127.0.0.1:8000/example.

To protect your own application, pull contracts for Python, or continue with the Direct HTTP integration for any other server stack.