Introduction
The MeetGrade API is a REST API that speaks JSON over HTTPS. All requests go to the base URL below. Every response is UTF-8 JSON; timestamps are ISO 8601.
https://app.meetgrade.com/api/v1
Version 1.0 is read-only — it lets you export data and react to events. Write endpoints are not part of this version.
Authentication
Authenticate every request with a Bearer token in the Authorization header. Create keys in the app under Settings → API keys.
Authorization: Bearer mg_live_xxxxxxxxxxxxxxxxxxxxxxxx
A key's secret is revealed only once, at creation. Copy it immediately — we store only a hash and can't show it again.
Never expose a key in browser code, mobile apps or public repos. Treat it like a password; revoke and rotate if leaked.
Rate limits
Each API key is limited to 120 requests per minute. Exceeding it returns HTTP 429 — back off and retry after a short pause.
/api/v1/calls
List your calls, newest first. Use limit and offset to paginate.
| Query param | Type | Description |
|---|---|---|
| limit | integer | Max items, ≤ 200. Default 50. |
| offset | integer | Items to skip. Default 0. |
curl https://app.meetgrade.com/api/v1/calls?limit=50 \
-H "Authorization: Bearer mg_live_xxx"
import requests
r = requests.get(
"https://app.meetgrade.com/api/v1/calls",
headers={"Authorization": "Bearer mg_live_xxx"},
params={"limit": 50, "offset": 0},
timeout=10,
)
data = r.json()
{
"data": [
{
"bot_id": "call_8f21ab",
"manager": "anna@acme.com",
"total_score": 87,
"checklist_id": "sales_phone_adult",
"key_result_achieved": true,
"created_at": "2026-06-22T14:05:11Z"
}
],
"total": 1284,
"limit": 50,
"offset": 0
}
/api/v1/calls/{id}
Fetch a single call with its full QA result: score, per-criterion breakdown, summary, strengths and issues.
curl https://app.meetgrade.com/api/v1/calls/call_8f21ab \
-H "Authorization: Bearer mg_live_xxx"
import requests
cid = "call_8f21ab"
r = requests.get(
f"https://app.meetgrade.com/api/v1/calls/{cid}",
headers={"Authorization": "Bearer mg_live_xxx"},
timeout=10,
)
call = r.json()
{
"bot_id": "call_8f21ab",
"manager": "anna@acme.com",
"created_at": "2026-06-22T14:05:11Z",
"qa": {
"total_score": 87,
"checklist_id": "sales_phone_adult",
"checklist_name": "Sales · phone",
"key_result_achieved": true,
"criteria": {
"greeting": { "score": 100, "comment": "Warm, on-brand opening." },
"needs_discovery": { "score": 70, "comment": "Probe needs earlier." }
},
"summary": "Strong rapport; closing step was vague.",
"strengths": ["Active listening", "Clear pricing"],
"issues": ["No confirmed next step"]
}
}
{ key: { score, comment } } — not an array. On legacy calls checklist_id may be null./api/v1/checklists
List your scoring checklists with their criteria and weights.
curl https://app.meetgrade.com/api/v1/checklists \
-H "Authorization: Bearer mg_live_xxx"
import requests
r = requests.get(
"https://app.meetgrade.com/api/v1/checklists",
headers={"Authorization": "Bearer mg_live_xxx"},
timeout=10,
)
checklists = r.json()["data"]
{
"data": [
{
"checklist_id": "sales_phone_adult",
"name": "Sales · phone",
"criteria": [
{ "key": "greeting", "weight": 15 },
{ "key": "needs_discovery", "weight": 25 },
{ "key": "next_step", "weight": 30 }
]
}
]
}
Errors
The API uses standard HTTP status codes. Error bodies are JSON with a human-readable detail field.
| Status | Meaning |
|---|---|
| 401 | Missing, malformed or revoked API key. |
| 404 | The requested call or resource doesn't exist (or isn't yours). |
| 429 | Rate limit exceeded — over 120 requests/min. Back off and retry. |
{ "detail": "Invalid or revoked API key." }
Webhooks
Instead of polling, let MeetGrade push to you. When a call finishes scoring, we fire a qa_completed event and POST a JSON payload to your configured URL.
Event: qa_completed
{
"bot_id": "call_8f21ab",
"score": 87,
"manager_email": "anna@acme.com",
"phone": "+380**********",
"checklist_id": "sales_phone_adult",
"key_result_achieved": true,
"call_url": "https://app.meetgrade.com/call/call_8f21ab"
}
Verifying the signature
Every delivery includes an X-StudyLess-Signature header — an HMAC-SHA256 of the raw request body, keyed with your webhook secret. Recompute it and compare before trusting the payload.
X-StudyLess-Signature: sha256=4f1c...e9a2
import hmac, hashlib
from flask import request, abort
WEBHOOK_SECRET = b"your_webhook_secret"
def verify(req):
sig = req.headers.get("X-StudyLess-Signature", "")
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET, req.get_data(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(sig, expected)
@app.post("/webhooks/meetgrade")
def handler():
if not verify(request):
abort(401)
event = request.get_json()
# ... handle qa_completed ...
return "", 200
Roadmap
More endpoints are coming — including team and per-manager analytics (aggregate scores, trends, top mistakes). Want early access? Email us at hello@meetgrade.com.