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.
Financial position, profit or loss, comprehensive income, cash flows and changes in equity.
Balance sheets must balance, subtotals must add up, cash and equity must roll forward. Anything that doesn't is flagged for review.
Each figure keeps its source page, the label and value exactly as printed, and its units.
REST API, webhooks, and exports to Excel, CSV and JSON for your own systems and dashboards.
You need an API key (it starts with sgk_). Contact us below to get one.
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
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"
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"
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"]
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.completed | Extracted and every check passed (or review finished). Figures are ready. |
| document.in_review | Extracted, but a check needs a person. See /v1/review-items. |
| document.analyzed | Statements were found but extraction couldn't run. |
| document.failed | Processing 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)
| Errors | Always {"error": {"code", "message", "request_id"}}. Quote the request ID when you contact us. |
| Rate limits | 300 requests and 30 uploads per minute per key. Over the limit you get 429 with Retry-After. |
| Duplicates | Uploading the same file again returns the existing document, so retries are safe. |
| Keys | Create one per environment with POST /v1/keys; revoke with POST /v1/keys/{id}/revoke. |