# Authentication

How to authenticate your API requests with Statsnet.

Statsnet authenticates API requests with API keys. A key looks like `sk_live_...` and can be passed in either of two headers — both are equivalent:

**Authentication headers**

```bash
x-api-key: sk_live_YOUR_KEY
# or
Authorization: Bearer sk_live_YOUR_KEY
```

## Getting a key

Create keys in your [account settings](https://statsnet.co/me) — up to 10 per account. The full key value is shown **once** at creation; store it securely. You can rename and revoke keys at any time, and see per-key usage for the current month.

If you don’t have a Statsnet account yet, [sign up](https://statsnet.co/auth?type=signup) — it takes a few minutes.

## What a key can access

A key inherits the subscription of the account that created it:

- **No active plan** — the key works, but only on public endpoints (company card `meta/beta`, search, markdown card).
- **Report plans** (Basic, Premium, Enterprise) — paid endpoints consume one report per company; repeat views of the same company within 2 weeks are free.
- **API plans** — every API call is metered against a monthly allowance with a burst limit. Current usage is returned on every response in the `X-API-Calls-Used` and `X-API-Calls-Limit` headers; exceeding the allowance returns `429`.

## Code Examples

**API request example**

**Python**

```python
import requests

url = 'https://statsnet.co/api/v1/companies/search'
headers = {
  'Authorization': 'Bearer sk_live_YOUR_KEY',
  'Content-Type': 'application/json'
}
body = {'query': 'kaspi', 'filters': {'jurisdiction': ['kz']}}

response = requests.post(url, json=body, headers=headers).json()
print(response)
```

**TypeScript**

```typescript
const url = 'https://statsnet.co/api/v1/companies/search';

const response = await fetch(url, {
method: 'POST',
headers: {
  'Authorization': 'Bearer sk_live_YOUR_KEY',
  'Content-Type': 'application/json',
},
body: JSON.stringify({ query: 'kaspi', filters: { jurisdiction: ['kz'] } }),
});

const data = await response.json();
console.log(data);
```

**Go**

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"io"
)

func main() {
	url := "https://statsnet.co/api/v1/companies/search"
	body, _ := json.Marshal(map[string]any{
		"query":   "kaspi",
		"filters": map[string]any{"jurisdiction": []string{"kz"}},
	})

	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
	req.Header.Set("Authorization", "Bearer sk_live_YOUR_KEY")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)
	fmt.Println(string(respBody))
}
```

**Rust**

```rust
use reqwest::Client;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
  let client = Client::new();
  let response = client
      .post("https://statsnet.co/api/v1/companies/search")
      .header("Authorization", "Bearer sk_live_YOUR_KEY")
      .header("Content-Type", "application/json")
      .json(&json!({"query": "kaspi", "filters": {"jurisdiction": ["kz"]}}))
      .send()
      .await?
      .text()
      .await?;

  println!("{}", response);
  Ok(())
}
```

**C#**

```csharp
using System.Net.Http;
using System.Text;
using System.Text.Json;

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post,
  "https://statsnet.co/api/v1/companies/search");

request.Headers.Add("Authorization", "Bearer sk_live_YOUR_KEY");
request.Content = new StringContent(
  JsonSerializer.Serialize(new { query = "kaspi" }),
  Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
```

**C++**

```cpp
#include <iostream>
#include <curl/curl.h>
#include <string>

size_t WriteCallback(void* contents, size_t size,
                   size_t nmemb, std::string* output) {
  output->append((char*)contents, size * nmemb);
  return size * nmemb;
}

int main() {
  CURL* curl = curl_easy_init();
  std::string response;

  struct curl_slist* headers = nullptr;
  headers = curl_slist_append(headers,
      "Authorization: Bearer sk_live_YOUR_KEY");
  headers = curl_slist_append(headers,
      "Content-Type: application/json");

  const char* body = R"({"query":"kaspi"})";

  curl_easy_setopt(curl, CURLOPT_URL,
      "https://statsnet.co/api/v1/companies/search");
  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

  curl_easy_perform(curl);
  std::cout << response << std::endl;

  curl_slist_free_all(headers);
  curl_easy_cleanup(curl);
  return 0;
}
```

## Error Handling

An invalid, revoked or missing key on a protected endpoint returns a JSON error with the matching HTTP status:

**Error response**

```json
{
  "status": 403,
  "message": "access denied"
}
```

| Status | Meaning |
| --- | --- |
| `401` | Session or credentials expired. |
| `402` | The key’s account has no active subscription (canceled or expired). |
| `403` | No access — anonymous caller on a protected endpoint, or the plan tier is too low. |
| `429` | Monthly API allowance exhausted or rate limit hit. Check `X-API-Calls-Used` / `X-API-Calls-Limit`. |
