genni.

Your documents hold your data.
Genni turns them into data you can use.

Send Genni annual reports and financial statements, digital or scanned. Get back every figure, mapped to a standard template, checked against accounting rules, and traceable to the page it came from.

Every statement

Financial position, profit or loss, comprehensive income, cash flows and changes in equity.

Checked, not guessed

Balance sheets must balance, subtotals must add up, cash and equity must roll forward. Anything that doesn't is flagged for review.

Traceable

Each figure keeps its source page, the label and value exactly as printed, and its units.

Built to plug in

REST API, webhooks, and exports to Excel, CSV and JSON for your own systems and dashboards.

Quickstart

You need an API key (it starts with sgk_). Contact us below to get one.

1. Upload a report
curl https://genni.scribblegreen.com/v1/documents \
  -H "Authorization: Bearer $GENNI_KEY" \
  -F file=@annual-report-2024.pdf \
  -F company_name="Dangote Cement Plc" -F fiscal_year=2024
2. Wait for completed (or in_review if a check needs a person). Use a webhook, or poll:
curl https://genni.scribblegreen.com/v1/documents/$DOC_ID -H "Authorization: Bearer $GENNI_KEY"
3. Get the figures, or the whole thing as a spreadsheet
curl "https://genni.scribblegreen.com/v1/documents/$DOC_ID/figures?statement=financial_position" \
  -H "Authorization: Bearer $GENNI_KEY"

curl -o report.xlsx https://genni.scribblegreen.com/v1/documents/$DOC_ID/export \
  -H "Authorization: Bearer $GENNI_KEY"
In Python:
import requests, time

API = "https://genni.scribblegreen.com/v1"
H = {"Authorization": f"Bearer {GENNI_KEY}"}

doc = requests.post(f"{API}/documents", headers=H,
                    files={"file": open("annual-report-2024.pdf", "rb")},
                    data={"company_name": "Nestle Nigeria Plc", "fiscal_year": 2024}).json()
while requests.get(f"{API}/documents/{doc['id']}", headers=H).json()["status"] in ("uploaded", "processing"):
    time.sleep(10)
figures = requests.get(f"{API}/documents/{doc['id']}/figures", headers=H).json()["items"]

Webhooks

Register an https URL and Genni will POST to it when a document changes status.

curl https://genni.scribblegreen.com/v1/webhooks -H "Authorization: Bearer $GENNI_KEY" \
  -H "Content-Type: application/json" -d '{"url": "https://your-app.example/genni"}'
# The response includes a secret (whsec_...) shown once. Keep it on your server.
document.completedExtracted and every check passed (or review finished). Figures are ready.
document.in_reviewExtracted, but a check needs a person. See /v1/review-items.
document.analyzedStatements were found but extraction couldn't run.
document.failedProcessing failed after retries.

Verify every delivery. The Genni-Signature header is t=<time>,v1=<HMAC-SHA256 of "t.body">:

import hmac, hashlib

def verify(secret: str, header: str, body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(parts["v1"], expected)

Good to know

ErrorsAlways {"error": {"code", "message", "request_id"}}. Quote the request ID when you contact us.
Rate limits300 requests and 30 uploads per minute per key. Over the limit you get 429 with Retry-After.
DuplicatesUploading the same file again returns the existing document, so retries are safe.
KeysCreate one per environment with POST /v1/keys; revoke with POST /v1/keys/{id}/revoke.