> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kolaria.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust and other languages

> The community Rust crate is no longer available. Generate a client from the OpenAPI document or call the API with reqwest.

<Warning>
  The community-maintained `notra` crate and its repository are no longer published. Kolaria does not ship an official Rust SDK. Use the OpenAPI document to generate a client, or call the REST API directly.
</Warning>

The API contract is published at [api.kolaria.com/openapi.json](https://api.kolaria.com/openapi.json) (OpenAPI 3.1). It is the same document the [API Reference](/api-reference/discovery/check-public-api-reachability) and the official [TypeScript SDK](/api/getting-started#sdk-installation) are built from, so a generated client matches the live endpoints, parameters and response shapes.

## Generate a client

Any OpenAPI 3.1 generator works. With [OpenAPI Generator](https://openapi-generator.tech):

```bash theme={null}
curl -s https://api.kolaria.com/openapi.json -o notra-openapi.json

openapi-generator-cli generate \
  -i notra-openapi.json \
  -g rust \
  -o notra-client \
  --additional-properties=packageName=notra,library=reqwest
```

Authentication is a single bearer scheme named `BearerAuth`, so configure the generated client's bearer token with your API key. Regenerate whenever you want to pick up new endpoints; the document is regenerated from the API source on every deploy.

## Call the API with reqwest

If you only need a few endpoints, a plain HTTP client is enough.

```toml Cargo.toml theme={null}
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
```

```rust src/main.rs theme={null}
use reqwest::Client;
use serde::Deserialize;

#[derive(Deserialize)]
struct Post {
    id: String,
    title: String,
    status: String,
}

#[derive(Deserialize)]
struct Pagination {
    #[serde(rename = "currentPage")]
    current_page: u32,
    #[serde(rename = "totalPages")]
    total_pages: u32,
}

#[derive(Deserialize)]
struct ListPostsResponse {
    posts: Vec<Post>,
    pagination: Pagination,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("KOLARIA_API_KEY")?;

    let response = Client::new()
        .get("https://api.kolaria.com/v1/posts")
        .bearer_auth(api_key)
        .query(&[("limit", "10"), ("status", "published")])
        .send()
        .await?
        .error_for_status()?
        .json::<ListPostsResponse>()
        .await?;

    for post in &response.posts {
        println!("{} ({}) {}", post.title, post.id, post.status);
    }

    println!(
        "Page {} of {}",
        response.pagination.current_page, response.pagination.total_pages
    );

    Ok(())
}
```

Field names in the JSON are camelCase; rename them with `serde` as shown or derive with `#[serde(rename_all = "camelCase")]`.

## What to expect from the API

* Base URL `https://api.kolaria.com`, header `Authorization: Bearer <key>`. See [Authentication](/api/authentication).
* Errors are JSON with an `error` string, plus `code` and `recovery` on authentication failures.
* Rate-limited endpoints return `429` with `RateLimit-*` and `Retry-After` headers. See [Rate Limits](/api/rate-limits).
* List endpoints for posts, feedback and GEO scans use `page` and `limit`. See [Pagination](/api/pagination).
