Query API
Every CLI command is sugar over one REST endpoint per entity:
POST /v2/{drugs|trials|companies|deals|news}/queryIt takes a query object: a predicate tree whose reference values are names or ids. The server resolves the names to Gosset ids, runs the query, and returns the results plus a resolved block showing what each name became. Use it directly when you want programmatic access (agents, notebooks, services) without resolving ids yourself.
Auth is the same Bearer token as everything else (Authorization: Bearer <token>).
The query object
{
"where": { "field": "target", "op": "is", "value": "TL1A" }, // predicate tree
"sort": "-latest_phase", // optional; entity default otherwise
"limit": 25,
"offset": 0,
"fields": ["name", "latest_phase", "developers"] // optional projection
}where is one of:
| Shape | Meaning |
|---|---|
{ "field": …, "op": …, "value": … } | a leaf predicate |
{ "and": [ … ] } | all sub-trees must match |
{ "or": [ … ] } | any sub-tree matches |
{ "not": … } | negation (see caveats) |
Ops: is, in (a list, or a comma string), contains (free-text search). A leaf value may be a single value or a list; a list means "any of".
Fields per entity
Reference fields (bold) accept a name or an id (a 24-hex ObjectId or a G[DMT]-… class id); everything else is matched verbatim.
| Entity | Reference fields | Other fields |
|---|---|---|
drugs | name/drug, target, disease, modality, company | phase, route, industry_only |
trials | drug, disease, target, sponsor | search, phase, status, registry, since, until, industry_only, has_results |
companies | name/company, disease, modality | country, type, stage, public |
deals | drug, target, disease, buyer, seller | deal_type, since, until, min_value |
news | drug, disease, company | search, category, since, until |
disease names resolve through the disease classifier (to disease_classes); the other reference fields resolve through autocomplete.
Reference resolution and the resolved echo
You pass names; the response tells you exactly what they matched. This request:
curl -s -X POST https://api.gosset.ai/v2/drugs/query \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"where":{"field":"target","value":"TL1A"},"limit":3,"fields":["name","latest_phase","developers"]}'returns (truncated):
{
"total": 49,
"results": [
{ "name": "afimkibart", "latest_phase": "3", "developers": ["Roche", "Roivant Sciences", "Pfizer", "Telavant"] },
{ "name": "duvakitug", "latest_phase": "3", "developers": ["Teva Pharmaceutical Industries", "Sanofi"] },
{ "name": "tulisokibart","latest_phase": "3", "developers": ["Schering-Plough", "Prometheus Biosciences"] }
],
"resolved": {
"target": {
"matched": [
{ "as": "autocomplete", "name": "Tumor Necrosis Factor-like Cytokine 1A (TL1A)", "ids": 14 }
],
"candidates": []
}
}
}resolved shows the matched entity name, how many ids it expanded to, and any runner-up candidates, so you can always see how a name was interpreted (for example whether "Merck" matched the entity you meant).
Unresolved names never widen the query
If a name matches nothing, the response is {"results": [], "total": 0, "message": "could not resolve …"}. A dropped filter never silently falls through to an unfiltered, whole-database result. The CLI surfaces this and exits 2.
Examples
# AND across fields: TROP2 drugs in breast cancer
-d '{"where":{"and":[
{"field":"target","value":"TROP2"},
{"field":"disease","value":"breast cancer"}]},"limit":25}'
# phase as a list (OR within a field)
-d '{"where":{"and":[
{"field":"target","value":"PD-1"},
{"field":"phase","op":"in","value":["Approved","3"]}]}}'
# free-text trial search + a structured filter
-d '{"where":{"and":[
{"field":"search","op":"contains","value":"semaglutide"},
{"field":"phase","value":"3"}]}}' // POST /v2/trials/query
# deals: parties, type, value, date
-d '{"where":{"and":[
{"field":"buyer","value":"Pfizer"},
{"field":"deal_type","value":"M&A"},
{"field":"min_value","value":"1000000000"},
{"field":"since","value":"2024-01-01"}]}}' // POST /v2/deals/queryCross-field OR
# every TL1A or TROP2 drug, in one call
-d '{"where":{"or":[
{"field":"target","value":"TL1A"},
{"field":"target","value":"TROP2"}]},"limit":25,"fields":["name","latest_phase"]}'{
"total": 149,
"note": "one or more OR-branches hit the 500-row cap; total is a lower bound",
"results": [
{ "name": "sacituzumab govitecan", "latest_phase": "Approved" },
{ "name": "datopotamab deruxtecan", "latest_phase": "Approved" }
]
}From the SDK
from gosset import GossetClient
client = GossetClient(api_key="…")
res = client.query(
"drugs",
where={"and": [
{"field": "target", "value": "TL1A"},
{"field": "phase", "value": "3"},
]},
sort="-latest_phase",
limit=25,
fields=["name", "latest_phase", "developers"],
)
print(res["total"], [r["name"] for r in res["results"]])Caveats
notreaching a leaf returns HTTP 501.notoverand/oris pushed inward (De Morgan), but the underlying filters have no negation seam yet, so a negated leaf is not supported. Negate at the value level where you can (for example an explicit phase list) instead.- OR totals can be a lower bound. A cross-field OR runs each branch and unions the results. If a branch hits the per-request row cap, the response carries a
noteandtotalis a floor, not an exact count. Narrow the branch or paginate for a complete set. totalsemantics follow the underlying entity. Trials, for instance, report-1when an exact count is not computed, matching the flat/v2/trials/endpoint.