Inventory reference
One feed. Your assigned catalog.
Read stock and product metadata for the SKUs made available to your key. Pull everything in one request, or filter by a comma-separated SKU list.
Read or Read/Write requiredRead-only endpointNo request body
Full inventory or specific SKUs
# Full inventory assigned to this key
GET https://hub.medidenta.com/api/inventory.php
# One product
GET https://hub.medidenta.com/api/inventory.php?sku=SKU-100
# Multiple products in one request
GET https://hub.medidenta.com/api/inventory.php?sku=SKU-100,SKU-200With no parameters, the endpoint returns all readable products in your key’s inventory scope, in a single response without pagination. “Full inventory” means your assigned catalog, not every product held by Medidenta.
Query parameters
| Parameter | Type | Required | Behavior |
|---|---|---|---|
sku | String | No | One SKU, or up to 100 comma-separated SKUs. Omit it for full inventory. Each SKU is limited to 100 bytes. |
- Values are trimmed, case-insensitive, and deduplicated. Preserve leading zeros:
000123is a SKU string. - An explicitly empty
?sku=, an array such assku[]=A, empty list entries, or unknown query parameters returns 400. - URL-encode special characters. For example, a plus sign must be
%2B. Use your client’s query encoder. - Do not send
skus, pagination parameters, JSON bodies, or authentication credentials in the query string.
Request examples
These examples look up two SKUs. Omit the filter, or set the SKU array to empty, to retrieve your full assigned catalog.
cURL
# Full inventory: omit --data-urlencode.
# Specific products: include the comma-separated sku parameter.
curl --silent --show-error --fail-with-body \
--user "$MEDIDENTA_CONSUMER_KEY:$MEDIDENTA_CONSUMER_SECRET" \
--header "Accept: application/json" \
--get "https://hub.medidenta.com/api/inventory.php" \
--data-urlencode "sku=SKU-100,SKU-200"JavaScript
// Server-side Node.js 18+; no browser credentials.
async function syncInventory() {
const key = process.env.MEDIDENTA_CONSUMER_KEY;
const secret = process.env.MEDIDENTA_CONSUMER_SECRET;
if (!key || !secret) throw new Error('Configure API credentials');
const url = new URL('https://hub.medidenta.com/api/inventory.php');
const skus = ['SKU-100', 'SKU-200']; // Use [] for full inventory.
if (skus.length) url.searchParams.set('sku', skus.join(','));
const response = await fetch(url, {
headers: {
Authorization: 'Basic ' + Buffer.from(key + ':' + secret).toString('base64'),
Accept: 'application/json'
}
});
const data = await response.json();
if (!response.ok) throw new Error(`HTTP ${response.status}: ${data.code}`);
console.log(data.meta.returned_count, data.products);
}
syncInventory().catch(error => {
console.error(error.message);
process.exitCode = 1;
});Python
# Python 3 standard library; no extra packages.
import os, json, base64
from urllib.request import Request, urlopen
from urllib.parse import urlencode
from urllib.error import HTTPError
key = os.environ['MEDIDENTA_CONSUMER_KEY']
secret = os.environ['MEDIDENTA_CONSUMER_SECRET']
auth = base64.b64encode(f'{key}:{secret}'.encode()).decode()
skus = ['SKU-100', 'SKU-200'] # Use [] for full inventory.
url = 'https://hub.medidenta.com/api/inventory.php'
if skus:
url += '?' + urlencode({'sku': ','.join(skus)})
request = Request(url, headers={
'Authorization': 'Basic ' + auth,
'Accept': 'application/json'
})
try:
with urlopen(request, timeout=60) as response:
data = json.load(response)
print(data['meta']['returned_count'], data['products'])
except HTTPError as error:
detail = json.loads(error.read())
raise RuntimeError(f"HTTP {error.code}: {detail.get('code')}") from errorPHP
<?php
// PHP 8+ with the cURL extension enabled.
$key = getenv('MEDIDENTA_CONSUMER_KEY');
$secret = getenv('MEDIDENTA_CONSUMER_SECRET');
if (!$key || !$secret) throw new RuntimeException('Configure API credentials');
$skus = ['SKU-100', 'SKU-200']; // Use [] for full inventory.
$url = 'https://hub.medidenta.com/api/inventory.php';
if ($skus) $url .= '?' . http_build_query(['sku' => implode(',', $skus)]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $key . ':' . $secret,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 60,
]);
$raw = curl_exec($ch);
if ($raw === false) throw new RuntimeException(curl_error($ch));
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if ($status >= 400) throw new RuntimeException('HTTP ' . $status . ': ' . $data['code']);
echo $data['meta']['returned_count'] . ' products returned';Successful response
All languages receive the same JSON structure. This full-inventory example contains illustrative product values, not live inventory.
{
"success": true,
"meta": {
"returned_count": 2,
"requested_count": null,
"not_found_count": 0,
"ambiguous_count": 0
},
"products": [
{
"sku": "SKU-100",
"status": "active",
"current_stock": 25,
"price": 19.95,
"images": [
"/uploads/products/example.jpg"
],
"name": "Example dental supply",
"dimensions": {
"length": 10,
"width": 5,
"height": 2,
"unit": "in"
},
"category": "Supplies",
"brand": "Example Brand"
},
{
"sku": "SKU-200",
"status": "inactive",
"current_stock": 0,
"price": null,
"images": [],
"name": "Another example product",
"dimensions": null,
"category": null,
"brand": null
}
],
"not_found": [],
"ambiguous_skus": [],
"request_timestamp": "2026-11-10T22:05:00Z"
}Product fields
| Field | Type | Meaning |
|---|---|---|
sku | String | Product identifier. Use this to match your product. |
status | String | active or inactive. Inactive products remain in scoped responses. |
current_stock | Number | Total quantity on hand across inventory locations and statuses. May include reserved or unavailable stock; zero when there are no balances. |
price | Number or null | Catalog selling price. Zero remains zero; an unset price is null. Currency is not included in this response. |
images | Array of strings | Primary image first, followed by sort order. Empty array if none. Local paths resolve against https://hub.medidenta.com, not the docs host. |
name | String or null | Catalog product name. |
dimensions | Object or null | Single-item dimensions: length, width, height, unit. Missing individual measurements remain null. Units are returned as stored; no conversion. |
category | String or null | Catalog category. |
brand | String or null | Catalog brand. |
Response metadata
| Field | Meaning |
|---|---|
meta.returned_count | Number of product objects actually returned. |
meta.requested_count | Unique requested SKUs for a filtered lookup; null for a full sync. |
meta.not_found_count | Number of requested SKUs not found or outside your scope. |
meta.ambiguous_count | Number of SKUs matching multiple products, omitted rather than guessed. |
not_found | Requested SKUs with no accessible match. |
ambiguous_skus | SKUs matching multiple products. |
request_timestamp | Server request-start time in UTC ISO 8601 format. Not an individual product’s update time. |
Sync and missing-item behavior
- Full inventory returns 200, including an empty assigned catalog.
- A filtered lookup returns 200 if at least one product matches. Inspect
not_foundfor missing or out-of-scope items. - If no requested products match, the endpoint returns 404 with
code: not_found. - If no unambiguous matches exist and duplicate SKU matches are present, it returns 409 with
code: ambiguous_sku. - Products follow request order for filtered lookups. Full inventory follows product ID order.
- GET requests never change stock. Zero stock and inactive products are still returned within scope.
- Use quantities as absolute values when syncing; do not add them to an existing quantity. Never clear your catalog because of a failed request or a filtered lookup.
{
"success": false,
"code": "not_found",
"message": "No products found for the requested SKUs.",
"meta": {
"returned_count": 0,
"requested_count": 1,
"not_found_count": 1,
"ambiguous_count": 0
},
"products": [],
"not_found": [
"UNKNOWN-SKU"
],
"ambiguous_skus": [],
"request_timestamp": "2026-11-10T22:05:00Z"
}Other responses: 400 for invalid parameters, 401 for invalid credentials, 403 for missing Read permission, 405 for non-GET methods, and 500 for a server error. See errors and retries.