Skip to content

Dwaprice Drug Prices API v1.0.0

Base URL https://api.dwaprice.com/v1 · JSON, UTF-8, snake_case · breaking changes only in /v2

Getting started

  1. Try the keyless sample: GET https://api.dwaprice.com/v1/sample/drugs?q=augmentin (50 fixed products, same shapes, "sample": true).
  2. Request access; once approved, create a key in the developer dashboard. The key is shown once.
  3. Call the real endpoints with Authorization: Bearer dp_live_…. Keep the key server-side; never ship it in a browser or mobile app.
First request
curl "https://api.dwaprice.com/v1/drugs/by-barcode/6221051011317" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

Public prices of medicines in Egypt (14,700+ products, one record per product and pack), with normalised companies, ingredients, categories, ATC codes, barcodes, alternatives, price history and a delta feed (/price-changes) for POS and ERP synchronisation.

  • JSON, UTF-8, snake_case. Numbers are numbers; unknown values are null, never "".
  • Dates are ISO 8601 (YYYY-MM-DD); timestamps are UTC (YYYY-MM-DDTHH:MM:SSZ).
  • Authentication: Authorization: Bearer dp_live_…. The /sample/* endpoints need no key and return a fixed set of about 50 drugs, marked "sample": true.
  • Breaking changes only in /v2. Clients must ignore unknown fields and enum values.

Full guide: https://dwaprice.com/api/docs

Authentication

Every non-sample request needs Authorization: Bearer dp_live_…. Missing, malformed, unknown, revoked or expired keys answer 401 unauthorized (the same answer: we do not reveal which). A valid key whose plan, scope or IP allowlist does not allow the endpoint answers 403 forbidden with details.required_scope or details.plan.

Keys: dp_live_ + 40 base32 characters, generated from 25 random bytes; only a SHA-256 hash is stored. Scopes: drugs:read, changes:read, export, webhooks. Rotation gives the old key a 24-hour grace period; revocation is immediate.

Pagination

Lists take limit (1–100, default 20) and cursor, and return page: { limit, has_more, next_cursor }. The cursor is an opaque signed string, valid 24 hours and bound to the sort and filters (changing a filter answers 400 invalid_cursor). Listings use keyset cursors that stay stable while the dataset changes; search results (q) are ranked and capped at 1,000. Lists carry no total count; /meta has the dataset counts.

Page 2
GET https://api.dwaprice.com/v1/drugs?dosage_form=syrup&limit=100&cursor=eyJmIjoi…

{ "data": [ … ], "page": { "limit": 100, "has_more": true, "next_cursor": "eyJmIjoi…" },
  "meta": { "dataset_version": "2026-10-21.1", "generated_at": "2026-10-21T14:36:16Z" } }

Errors

One shape for every non-2xx answer: { "error": { "code", "message", "details" } }. message is for developers; details is structured (an object, an array of { param, issue } for validation, or null).

Errors
400 validation_errorType: details[]Every invalid parameter, with param and issue.
400 invalid_cursorType: —Cursor expired, malformed, or from other filters.
400 since_too_oldType: window_dayssince is outside the plan’s feed window: reload with /drugs.
401 unauthorizedType: —Missing, malformed, unknown, revoked or expired key.
403 forbiddenType: plan, required_scope, reasonPlan, scope, suspended account or expired subscription.
403 ip_not_allowedType: —Enterprise IP allowlist.
404 not_foundType: —Unknown id or path.
301 movedType: new_id + LocationThe id was retired; follow Location.
429 rate_limitedType: retry_afterPer-minute limit; Retry-After header.
429 quota_exceededType: resets_atMonthly quota; Retry-After until the month resets.
500 internal_errorType: request_idOur fault; quote request_id to support.
503 unavailableType: —Maintenance or database unreachable; Retry-After.

Rate limits

Per key: a per-minute limit and a monthly quota (calendar month, UTC), both from your plan. The sample is limited per source network (30/min, 300/day). Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix seconds) and, for keys, X-Quota-Limit, X-Quota-Remaining, X-Quota-Reset. Over the limit: 429 with Retry-After. A 304 or 4xx counts as a request; a 5xx does not. Send If-None-Match with the ETag you received to get a free 304 when nothing changed.

Delta-sync guide

The feed GET /price-changes?since= returns append-only events ordered by id: price_changed, added, status_changed, removed, renamed. Each event carries the current record (drug), so one call updates your copy.

  • First sync: load the catalogue with GET /drugs (all pages) and store meta.generated_at.
  • Every N minutes: GET /price-changes?since=<stored>; page with cursor (it carries the original since); apply each event; store latest_detected_at.
  • since is inclusive and delivery is at-least-once: skip event ids you already applied.
  • since older than your plan's window answers 400 since_too_old: do a full reload.
Sync loop (JavaScript)
let since = stored.latest_detected_at;        // from the previous run
let cursor = null;
do {
  const url = cursor ? `https://api.dwaprice.com/v1/price-changes?cursor=${encodeURIComponent(cursor)}`
                     : `https://api.dwaprice.com/v1/price-changes?since=${since}&limit=100`;
  const page = await (await fetch(url, { headers: { Authorization: "Bearer " + KEY } })).json();
  for (const e of page.data) {
    if (e.id <= stored.last_event_id) continue;       // at-least-once: skip what you applied
    applyEvent(e);                                     // e.type, e.data, e.drug (current record)
    stored.last_event_id = e.id;
  }
  if (page.latest_detected_at) stored.latest_detected_at = page.latest_detected_at;
  cursor = page.page.next_cursor;
} while (cursor);

Drugs

Products, lookups, alternatives and price history.

GET/drugs

List or search drugs

Without q: the catalogue, filtered and sorted (keyset cursor, stable across updates). With q: typo-tolerant search over Arabic and English trade names, approved aliases and spelling variants, active ingredients and barcodes; ranked, at most 1,000 results.

Parameters

GET /drugs parameters
q (query)Type: stringSearch text, 2–80 characters (Arabic or English; typo-tolerant).كونجستل
company (query)Type: stringCompany id (see `/companies`).sigma
ingredient (query)Type: stringIngredient id (see `/ingredients`); products containing it.paracetamol
atc (query)Type: stringATC code prefix, matched against product-level and ingredient-level codes (`J01` matches `J01CR02`).N02BE
dosage_form (query)Type: DosageFormClass
route (query)Type: RouteClass
prescription_status (query)Type: PrescriptionStatus
status (query)Type: ListingStatusDefault is all statuses.
has_barcode (query)Type: boolean
updated_since (query)Type: string (date-time)Records whose `updated_at` is after this timestamp (any field change). For price changes alone use `/price-changes`.
sort (query)Type: name | price | -price | updated_at | -updated_atIgnored when `q` is given (results are ranked by relevance).default "name"
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 A page of drugs. → DrugList
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/drugs" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/drugs/{id}

One drug by id

Parameters

GET /drugs/{id} parameters
id (path)*Type: stringStable public id (the product's URL slug).congestal-20-tabs

Responses

  • 200 The drug. → DrugResponse
  • 301 The id was retired (product renamed in the source list). `Location` points at the current id. → Error
    example
    {
      "error": {
        "code": "moved",
        "message": "This id was replaced by congestal-20-tabs",
        "details": {
          "new_id": "congestal-20-tabs"
        }
      }
    }
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 404 Unknown id or path. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/drugs/congestal-20-tabs" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/drugs/by-barcode/{barcode}

Drugs by barcode

Returns a list: in the source price lists 73 barcodes are shared by more than one pack (e.g. two pack sizes of one product). Ordered listed first, then newest price date. An unknown barcode gives an empty list.

Parameters

GET /drugs/by-barcode/{barcode} parameters
barcode (path)*Type: string6221051011317

Responses

  • 200 Matching drugs (possibly none). → DrugArrayResponse
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/drugs/by-barcode/6221051011317" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/drugs/{id}/alternatives

Alternatives with the same ingredients

Products with the same active ingredients and the same strength, cheapest first (no price last). strength=any appends products with the same ingredients at another strength.

Parameters

GET /drugs/{id}/alternatives parameters
id (path)*Type: stringStable public id (the product's URL slug).congestal-20-tabs
strength (query)Type: same | anydefault "same"
limit (query)Type: integerdefault 50
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 Alternatives. → AlternativeList
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 404 Unknown id or path. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/drugs/congestal-20-tabs/alternatives" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/drugs/{id}/price-history

Price history

Oldest first; the last point is the current price. Consecutive identical prices are collapsed.

Parameters

GET /drugs/{id}/price-history parameters
id (path)*Type: stringStable public id (the product's URL slug).congestal-20-tabs

Responses

  • 200 Price points. → PriceHistoryResponse
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 404 Unknown id or path. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/drugs/congestal-20-tabs/price-history" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

Changes

The delta feed for incremental synchronisation.

GET/price-changes

Delta feed of changes since a time

Append-only events ordered by id: price_changed, added, status_changed, removed, renamed. Each event carries the current record, so one call updates a local copy. Sync loop: store latest_detected_at, call again with it as since, page with cursor, skip event ids already applied (delivery is at-least-once). since older than the plan's feed window → 400 since_too_old (reload with /drugs). Requires scope changes:read.

Parameters

GET /price-changes parameters
since (query)Type: string (date-time)Events with `detected_at` at or after this timestamp (inclusive; skip event ids already applied). Required unless `cursor` is given — a cursor carries the original `since`.2026-10-21T00:00:00Z
type (query)Type: stringComma-separated event types; default all.price_changed,status_changed
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 A page of events. → PriceChangeList
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/price-changes" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

Reference

Companies and active ingredients.

GET/companies

Companies

Parameters

GET /companies parameters
q (query)Type: stringName prefix, case-insensitive.
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 Companies sorted by name. → CompanyList
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/companies" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/ingredients

Active ingredients

Parameters

GET /ingredients parameters
q (query)Type: stringArabic or English name prefix.
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 Ingredients sorted by English name. → IngredientList
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/ingredients" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

Meta

Dataset version and counts.

GET/meta

Dataset version, update time and counts

Responses

  • 200 Dataset metadata. → MetaResponse
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/meta" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

Sample

Free, keyless access to a fixed sample of about 50 drugs. Same shapes as the real endpoints.

GET/sample/drugsno key

List or search the sample drugs

Same parameters as /drugs; results come only from the fixed sample set. Rate limit per IP (30/min, 300/day).

Parameters

GET /sample/drugs parameters
q (query)Type: stringSearch text, 2–80 characters (Arabic or English; typo-tolerant).كونجستل
company (query)Type: string
ingredient (query)Type: string
atc (query)Type: string
dosage_form (query)Type: DosageFormClass
prescription_status (query)Type: PrescriptionStatus
sort (query)Type: name | price | -price | updated_at | -updated_atdefault "name"
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 A page of sample drugs. → SampleDrugList
  • 400 Invalid parameter, cursor or `since`. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/drugs"

GET/sample/drugs/{id}no key

One sample drug

Parameters

GET /sample/drugs/{id} parameters
id (path)*Type: stringStable public id (the product's URL slug).congestal-20-tabs

Responses

  • 200 The drug. → SampleDrugResponse
  • 404 Unknown id or path. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/drugs/congestal-20-tabs"

GET/sample/drugs/by-barcode/{barcode}no key

Sample drugs by barcode

Parameters

GET /sample/drugs/by-barcode/{barcode} parameters
barcode (path)*Type: string

Responses

  • 200 Matching sample drugs (possibly none). → SampleDrugArrayResponse
  • 400 Invalid parameter, cursor or `since`. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/drugs/by-barcode/6221051011317"

GET/sample/drugs/{id}/alternativesno key

Alternatives inside the sample set

Parameters

GET /sample/drugs/{id}/alternatives parameters
id (path)*Type: stringStable public id (the product's URL slug).congestal-20-tabs
strength (query)Type: same | anydefault "same"

Responses

  • 200 Alternatives that are themselves sample drugs. → SampleAlternativeList
  • 404 Unknown id or path. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/drugs/congestal-20-tabs/alternatives"

GET/sample/drugs/{id}/price-historyno key

Price history of a sample drug

Parameters

GET /sample/drugs/{id}/price-history parameters
id (path)*Type: stringStable public id (the product's URL slug).congestal-20-tabs

Responses

  • 200 Price points. → SamplePriceHistoryResponse
  • 404 Unknown id or path. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/drugs/congestal-20-tabs/price-history"

GET/sample/price-changesno key

Delta feed restricted to the sample drugs

Parameters

GET /sample/price-changes parameters
since (query)Type: string (date-time)
type (query)Type: string
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 Events for sample drugs only. → SamplePriceChangeList
  • 400 Invalid parameter, cursor or `since`. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/price-changes"

GET/sample/companiesno key

Companies of the sample drugs

Parameters

GET /sample/companies parameters
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 Companies. → SampleCompanyList
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/companies"

GET/sample/ingredientsno key

Ingredients of the sample drugs

Parameters

GET /sample/ingredients parameters
limit (query)Type: integerdefault 20
cursor (query)Type: stringOpaque cursor from `page.next_cursor` of the previous response.

Responses

  • 200 Ingredients. → SampleIngredientList
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/ingredients"

GET/sample/metano key

Dataset metadata (counts of the sample set)

Responses

  • 200 Metadata. → SampleMetaResponse
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/sample/meta"

Enterprise

Bulk export and webhooks (Enterprise plan).

POST/exports

Request a full dataset export (CSV or JSON)

One export per dataset version is generated and reused. Requires scope export (Enterprise plan).

Responses

  • 202 Export queued (or already ready). → ExportResponse
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 429 Per-minute limit or monthly quota exceeded; `Retry-After` in seconds. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/exports" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/exports/{id}

Export status and signed download URL

Parameters

GET /exports/{id} parameters
id (path)*Type: string

Responses

  • 200 Export status; `url` is a signed link valid for one hour once `status` is `ready`. → ExportResponse
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 404 Unknown id or path. → Error
  • 500 Our fault; quote `request_id` to support. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/exports/congestal-20-tabs" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

GET/webhooks

Registered webhooks

Responses

  • 200 Webhooks of the client. → object
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/webhooks" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

POST/webhooks

Register a webhook

Batches of events are POSTed within 10 minutes of each dataset capture, signed with X-Dwaprice-Signature: sha256=<HMAC-SHA256 of the raw body with the secret> and X-Dwaprice-Delivery: <id>. Non-2xx answers are retried with exponential back-off for 24 hours; then the webhook is paused and the admin alerted. Requires scope webhooks.

Responses

  • 201 Created; `secret` is shown only in this response. → object
  • 400 Invalid parameter, cursor or `since`. → Error
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/webhooks" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

DELETE/webhooks/{id}

Delete a webhook

Parameters

DELETE /webhooks/{id} parameters
id (path)*Type: string

Responses

  • 204 Deleted.
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 404 Unknown id or path. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/webhooks/congestal-20-tabs" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

POST/webhooks/{id}/test

Send a signed test delivery

Parameters

POST /webhooks/{id}/test parameters
id (path)*Type: string

Responses

  • 200 The receiver's status code and latency. → object
  • 401 Missing, malformed, unknown, revoked or expired key. → Error
  • 403 Key valid, but the plan, scope or IP does not allow this request. → Error
  • 404 Unknown id or path. → Error
  • Error bodies: see Errors.
curl
curl "https://api.dwaprice.com/v1/webhooks/congestal-20-tabs/test" \
  -H "Authorization: Bearer dp_live_YOUR_KEY"

Schemas

Error

Error fields
error*Type: object
error.code*Type: validation_error | invalid_cursor | since_too_old | unauthorized | forbidden | ip_not_allowed | not_found | moved | rate_limited | quota_exceeded | internal_error | unavailable
error.message*Type: string
error.details*Type: object | object[] | nullStructured context (object or array) or null.

Page

Page fields
limit*Type: integer
has_more*Type: boolean
next_cursor*Type: string | nullOpaque; pass as `cursor`. Null on the last page.

Meta

Meta fields
dataset_version*Type: string2026-10-21.184
generated_at*Type: string (date-time)

DosageFormClass

One of: tablet, capsule, injection, syrup, suspension, drops, sachet, cream, ointment, gel, lotion, solution, spray, powder, inhaler, suppository, other

RouteClass

One of: oral, topical, injection, ophthalmic, vaginal, rectal, nasal, inhalation, otic, other

PrescriptionStatus

One of: prescription, otc, not_applicable

ListingStatus

The price list's own marker on the line. `listed` = no marker; it is not a stock indication.

One of: listed, not_available, not_yet_available, cancelled, illegal_import

EventType

One of: price_changed, added, status_changed, removed, renamed

Ingredient

Ingredient fields
id*Type: string | nullparacetamol
name_en*Type: string | nullParacetamol (acetaminophen)
name_ar*Type: string | nullباراسيتامول
strength*Type: string | null650 mg
atc_code*Type: string | nullN02BE01

Price

Price fields
current*Type: number | null50
previous*Type: number | null31
unit_price*Type: number | nullPrice per tablet, capsule, sachet… as listed.2.5
currency*Type: "EGP"
effective_date*Type: string (date) | nullDate of the current price on the price list.
updated_at*Type: string (date-time) | nullWhen the API recorded the current price (sync key). Null only when there is no price.
verified*Type: booleanChecked against a published source.
verified_at*Type: string (date) | null

NamedRef

NamedRef fields
id*Type: stringsigma
name*Type: stringSigma

Category

Category fields
id*Type: stringcold
en*Type: string | nullCold/cough combination (analgesic + antihistamine + decongestant)
ar*Type: string | null

DosageForm

DosageForm fields
class*Type: DosageFormClass
en*Type: string | nullTablet
ar*Type: string | nullقرص

Drug

Drug fields
id*Type: stringStable public id (URL slug).congestal-20-tabs
trade_name_en*Type: stringCongestal 20 tabs
trade_name_ar*Type: string | nullكونجستال 20 قرص
scientific_name*Type: string | nullChlorpheniramine+paracetamol(acetaminophen)+pseudoephedrine
ingredients*Type: Ingredient[]
strength*Type: string | null650/4/60 mg
dosage_form*Type: DosageForm
dosage_form.class*Type: DosageFormClass
dosage_form.en*Type: string | nullTablet
dosage_form.ar*Type: string | nullقرص
route*Type: RouteClass | null
route_text*Type: string | nullOral
units_per_package*Type: integer | null20
packaging*Type: string | null20 tablets (2 blisters of 10)
price*Type: Price
price.current*Type: number | null50
price.previous*Type: number | null31
price.unit_price*Type: number | nullPrice per tablet, capsule, sachet… as listed.2.5
price.currency*Type: "EGP"
price.effective_date*Type: string (date) | nullDate of the current price on the price list.
price.updated_at*Type: string (date-time) | nullWhen the API recorded the current price (sync key). Null only when there is no price.
price.verified*Type: booleanChecked against a published source.
price.verified_at*Type: string (date) | null
barcode*Type: string | null6221051011317
company*Type: NamedRef | nullMarketing company when known, else the manufacturer.
manufacturer*Type: NamedRef | nullSet when a separate manufacturer is listed.
category*Type: Category | null
atc_codes*Type: string[]Product-level ATC codes; ingredient-level codes are on `ingredients[]`.
prescription_status*Type: PrescriptionStatus | null
status*Type: ListingStatusThe price list's own marker on the line. `listed` = no marker; it is not a stock indication.
price_list_name*Type: stringThe exact line name on the source price list.Congestal 20 tabs
created_at*Type: string (date-time)
updated_at*Type: string (date-time)Last change of any field.
links*Type: object
links.web_url*Type: string (uri)https://dwaprice.com/dawa/congestal-20-tabs
example
{
  "id": "congestal-20-tabs",
  "trade_name_en": "Congestal 20 tabs",
  "trade_name_ar": "كونجستال 20 قرص",
  "scientific_name": "Chlorpheniramine+paracetamol(acetaminophen)+pseudoephedrine",
  "ingredients": [
    {
      "id": "paracetamol",
      "name_en": "Paracetamol (acetaminophen)",
      "name_ar": "باراسيتامول",
      "strength": "650 mg",
      "atc_code": "N02BE01"
    },
    {
      "id": "chlorpheniramine",
      "name_en": "Chlorphenamine maleate",
      "name_ar": "كلورفينيرامين ماليات",
      "strength": "4 mg",
      "atc_code": "R06AB04"
    },
    {
      "id": "pseudoephedrine",
      "name_en": "Pseudoephedrine hydrochloride",
      "name_ar": "سودوإيفيدرين هيدروكلوريد",
      "strength": "60 mg",
      "atc_code": "R01BA02"
    }
  ],
  "strength": "650/4/60 mg",
  "dosage_form": {
    "class": "tablet",
    "en": "Tablet",
    "ar": "قرص"
  },
  "route": "oral",
  "route_text": "Oral",
  "units_per_package": 20,
  "packaging": "20 tablets (2 blisters of 10)",
  "price": {
    "current": 50,
    "previous": 31,
    "unit_price": 2.5,
    "currency": "EGP",
    "effective_date": "2024-06-06",
    "updated_at": "2026-11-01T00:00:00Z",
    "verified": true,
    "verified_at": "2026-10-03"
  },
  "barcode": "6221051011317",
  "company": {
    "id": "sigma",
    "name": "Sigma"
  },
  "manufacturer": null,
  "category": {
    "id": "cold",
    "en": "Cold/cough combination (analgesic + antihistamine + decongestant)",
    "ar": null
  },
  "atc_codes": [],
  "prescription_status": "otc",
  "status": "listed",
  "price_list_name": "Congestal 20 tabs",
  "created_at": "2026-11-01T00:00:00Z",
  "updated_at": "2026-11-01T00:00:00Z",
  "links": {
    "web_url": "https://dwaprice.com/dawa/congestal-20-tabs"
  }
}

Alternative

Alternative fields
id*Type: stringStable public id (URL slug).congestal-20-tabs
trade_name_en*Type: stringCongestal 20 tabs
trade_name_ar*Type: string | nullكونجستال 20 قرص
scientific_name*Type: string | nullChlorpheniramine+paracetamol(acetaminophen)+pseudoephedrine
ingredients*Type: Ingredient[]
strength*Type: string | null650/4/60 mg
dosage_form*Type: DosageForm
dosage_form.class*Type: DosageFormClass
dosage_form.en*Type: string | nullTablet
dosage_form.ar*Type: string | nullقرص
route*Type: RouteClass | null
route_text*Type: string | nullOral
units_per_package*Type: integer | null20
packaging*Type: string | null20 tablets (2 blisters of 10)
price*Type: Price
price.current*Type: number | null50
price.previous*Type: number | null31
price.unit_price*Type: number | nullPrice per tablet, capsule, sachet… as listed.2.5
price.currency*Type: "EGP"
price.effective_date*Type: string (date) | nullDate of the current price on the price list.
price.updated_at*Type: string (date-time) | nullWhen the API recorded the current price (sync key). Null only when there is no price.
price.verified*Type: booleanChecked against a published source.
price.verified_at*Type: string (date) | null
barcode*Type: string | null6221051011317
company*Type: NamedRef | nullMarketing company when known, else the manufacturer.
manufacturer*Type: NamedRef | nullSet when a separate manufacturer is listed.
category*Type: Category | null
atc_codes*Type: string[]Product-level ATC codes; ingredient-level codes are on `ingredients[]`.
prescription_status*Type: PrescriptionStatus | null
status*Type: ListingStatusThe price list's own marker on the line. `listed` = no marker; it is not a stock indication.
price_list_name*Type: stringThe exact line name on the source price list.Congestal 20 tabs
created_at*Type: string (date-time)
updated_at*Type: string (date-time)Last change of any field.
links*Type: object
links.web_url*Type: string (uri)https://dwaprice.com/dawa/congestal-20-tabs
match*Type: same_strength | other_strength
same_form*Type: booleanSame dosage-form class as the requested drug.
same_product*Type: booleanAnother pack of the same product.
price_diff*Type: number | nullalternative.price.current − drug.price.current
unit_price_diff_pct*Type: number | nullUnit-price difference in percent (negative = cheaper per unit).

PricePoint

PricePoint fields
price*Type: number
effective_date*Type: string (date) | null
recorded_at*Type: string (date-time) | null
source*Type: catalog | change_log

PriceChangeEvent

PriceChangeEvent fields
id*Type: integerMonotonic ordering key.
type*Type: EventType
detected_at*Type: string (date-time)
drug_id*Type: string
data*Type: objectprice_changed: {old, new, effective_date, change_pct}; added: {price}; status_changed: {old, new}; removed: {last_price, last_status}; renamed: {new_id}
drug*Type: Drug | nullThe current record; null for `removed`.
example
{
  "id": 48213,
  "type": "price_changed",
  "detected_at": "2026-10-21T12:22:04Z",
  "drug_id": "congestal-20-tabs",
  "data": {
    "old": 31,
    "new": 50,
    "effective_date": "2024-06-06",
    "change_pct": 61.29
  },
  "drug": {
    "id": "congestal-20-tabs",
    "trade_name_en": "Congestal 20 tabs",
    "...": "..."
  }
}

Company

Company fields
id*Type: stringeva-pharma
name*Type: stringEva Pharma
product_count*Type: integer305

IngredientSummary

IngredientSummary fields
id*Type: stringparacetamol
name_en*Type: string | nullParacetamol
name_ar*Type: string | nullباراسيتامول
product_count*Type: integer179

DatasetMeta

DatasetMeta fields
dataset_version*Type: string2026-10-21.184
updated_at*Type: string (date-time)
last_price_change_at*Type: string (date-time) | null
last_price_list_date*Type: string (date) | null
counts*Type: object
counts.drugs*Type: integer14726
counts.priced*Type: integer14169
counts.verified*Type: integer13597
counts.with_barcode*Type: integer5302
counts.companies*Type: integer3270
counts.ingredients*Type: integer3771
counts.categories*Type: integer987
api_version*Type: string1.0.0
docs_url*Type: string (uri)

Webhook

Webhook fields
id*Type: string
url*Type: string (uri)
events*Type: EventType[]
status*Type: active | paused
created_at*Type: string (date-time)
last_delivery_at*Type: string (date-time) | null
last_status*Type: integer | null

Changelog

  • 1.0.0 — 2026-10-21

    Initial release: drugs, search, barcode lookup, alternatives, price history, delta feed, companies, ingredients, meta; keyless sample; Enterprise exports and webhooks.

Additive changes (new fields, parameters, endpoints, enum values) ship in /v1 and are listed here; clients must ignore unknown fields and values. Breaking changes ship in /v2 with /v1 kept for 12 months and announced with Deprecation and Sunset headers.