{"slug": "the-3-step-smoke-test-i-use-for-any-openai-compatible-api", "title": "The 3-step smoke test I use for any OpenAI-compatible API", "summary": "A developer at DaoXE published a three-step smoke test for verifying OpenAI-compatible API endpoints. The test isolates authentication, model availability, and a minimal chat completion call using the OpenAI SDK. The approach is designed to quickly confirm that a specific account, endpoint, model, and runtime can complete a single request.", "body_md": "Disclosure and availability:DaoXE is a service we operate. It is not available in mainland China. This article is for developers in regions allowed by the service terms.\n\nChanging `baseURL`\n\nin an OpenAI SDK takes one line. Proving that the new endpoint is configured correctly is the harder part.\n\nWhen an integration fails, it is tempting to change the API key, model, prompt, timeout, and response parser at the same time. That usually creates more uncertainty. I prefer a three-step smoke test that isolates one layer at a time:\n\nThe result is not a benchmark or a reliability guarantee. It is a repeatable way to answer a narrower question: *can this exact account, endpoint, model, and runtime complete one request right now?*\n\nThis walkthrough uses DaoXE as the example endpoint, but the same sequence works with other OpenAI-compatible services.\n\nStart with the API key and base URL. Do not put a real key in source code, screenshots, issue reports, or shell snippets that will be committed.\n\n```\nexport DAOXE_API_KEY=\"your_api_key\"\nexport DAOXE_BASE_URL=\"https://daoxe.com/v1\"\n```\n\nDo not set the model yet. Model access can vary by account and can change over time. Copying a model name from an old tutorial adds an unnecessary variable before authentication has even been checked.\n\nIf you use a `.env`\n\nfile locally, make sure it is ignored by Git. In production, use the secret manager provided by your deployment platform.\n\nCall the Models endpoint first:\n\n```\ncurl --fail-with-body --show-error --silent \\\n  \"${DAOXE_BASE_URL}/models\" \\\n  -H \"Authorization: Bearer ${DAOXE_API_KEY}\"\n```\n\nThis request checks two things without running a generation:\n\nIf `jq`\n\nis installed, reduce the response to model IDs:\n\n```\ncurl --fail-with-body --show-error --silent \\\n  \"${DAOXE_BASE_URL}/models\" \\\n  -H \"Authorization: Bearer ${DAOXE_API_KEY}\" \\\n  | jq -r '.data[].id'\n```\n\nChoose one ID from that response and copy it exactly:\n\n```\nexport DAOXE_MODEL=\"copy_an_exact_id_from_the_current_response\"\n```\n\nAvoid permanently hard-coding a model name from documentation. A successful request in somebody else's account does not prove that the same ID is available to your account today.\n\nCreate an empty Node.js project and install the OpenAI SDK:\n\n```\nnpm init -y\nnpm install openai\n```\n\nCreate `smoke-test.mjs`\n\n:\n\n``` python\nimport OpenAI from \"openai\";\n\nconst required = [\"DAOXE_API_KEY\", \"DAOXE_BASE_URL\", \"DAOXE_MODEL\"];\nconst missing = required.filter((name) => !process.env[name]);\n\nif (missing.length > 0) {\n  console.error(`Missing environment variables: ${missing.join(\", \")}`);\n  process.exit(1);\n}\n\nconst client = new OpenAI({\n  apiKey: process.env.DAOXE_API_KEY,\n  baseURL: process.env.DAOXE_BASE_URL,\n  timeout: 30_000,\n  maxRetries: 0,\n});\n\nconst startedAt = performance.now();\n\ntry {\n  const response = await client.chat.completions.create({\n    model: process.env.DAOXE_MODEL,\n    messages: [{ role: \"user\", content: \"Reply with only OK.\" }],\n    max_tokens: 8,\n  });\n\n  const elapsedMs = Math.round(performance.now() - startedAt);\n  console.log({\n    ok: true,\n    elapsedMs,\n    model: response.model,\n    content: response.choices?.[0]?.message?.content,\n    usage: response.usage,\n  });\n} catch (error) {\n  const elapsedMs = Math.round(performance.now() - startedAt);\n  console.error({\n    ok: false,\n    elapsedMs,\n    status: error.status,\n    type: error.name,\n  });\n  process.exit(1);\n}\n```\n\nRun it:\n\n```\nnode smoke-test.mjs\n```\n\nThis request can incur a charge. Check the live model catalog, account status, and pricing before running it.\n\nThe `max_tokens: 8`\n\nlimit keeps the connectivity check small; it is not a recommendation for normal application traffic. I also set `maxRetries: 0`\n\non purpose. During first-pass debugging, an automatic retry can hide the original failure or turn one uncertain request into several billable requests.\n\nA successful run proves that, at that moment, the following combination completed one request:\n\nIt does **not** prove long-term availability, output quality, latency distribution, or total application cost.\n\nFor a useful test record, keep the timestamp, environment, HTTP result, elapsed time, returned model field, and token usage. Do not log the API key or the complete `Authorization`\n\nheader.\n\n| Symptom | Check first | Next action |\n|---|---|---|\n`401` |\nEmpty, expired, or whitespace-padded key | Re-inject the key securely, then repeat the Models request |\n`403` |\nAccount permissions or regional eligibility | Review the account state and service terms; do not try to bypass the restriction in code |\n`404` |\nVersioned `baseURL` and combined resource path |\nPrint only non-sensitive URL configuration and inspect the final path |\n| Model not found | Whether the ID came from the current Models response | Discover the models again and copy the ID exactly |\n`429` |\nCurrent rate, quota, and account state | Read the response details before choosing a backoff policy |\n| Timeout or network error | DNS, TLS, proxy, and runtime egress | Run the Models request from the same environment to separate network and generation failures |\n`5xx` |\nTemporary service or upstream failure | Save the timestamp and request identifier, then retry a limited number of times later |\n\nThe important rule is to change one layer at a time. If authentication is failing, changing the prompt cannot help. If model discovery works but generation fails, the problem is already narrower.\n\nRetries are not automatically safe. A request may have executed on the server even when the client did not receive the response. Blindly retrying can duplicate work and cost.\n\nBefore enabling retries, decide:\n\nI split CI into two layers:\n\nThat catches code regressions without sending a billable request on every commit. Secret masking is helpful, but the safer default is still never to print the secret.\n\nFor any OpenAI-compatible endpoint, the shortest useful validation sequence is:\n\nIf your region is supported by the service terms, you can inspect [DaoXE](https://daoxe.com/?utm_source=devto&utm_medium=organic&utm_campaign=global_launch). The public examples include [cURL, Node.js, Python, and Postman](https://github.com/seven7763/DaoXE-AI), plus [Simplified Chinese](https://github.com/seven7763/DaoXE-AI/blob/main/README.zh-CN.md) and [Traditional Chinese](https://github.com/seven7763/DaoXE-AI/blob/main/README.zh-TW.md) guides.", "url": "https://wpnews.pro/news/the-3-step-smoke-test-i-use-for-any-openai-compatible-api", "canonical_source": "https://dev.to/seven7763/the-3-step-smoke-test-i-use-for-any-openai-compatible-api-311g", "published_at": "2026-07-12 05:48:06+00:00", "updated_at": "2026-07-12 06:13:46.753029+00:00", "lang": "en", "topics": ["developer-tools", "large-language-models", "ai-tools"], "entities": ["DaoXE", "OpenAI"], "alternates": {"html": "https://wpnews.pro/news/the-3-step-smoke-test-i-use-for-any-openai-compatible-api", "markdown": "https://wpnews.pro/news/the-3-step-smoke-test-i-use-for-any-openai-compatible-api.md", "text": "https://wpnews.pro/news/the-3-step-smoke-test-i-use-for-any-openai-compatible-api.txt", "jsonld": "https://wpnews.pro/news/the-3-step-smoke-test-i-use-for-any-openai-compatible-api.jsonld"}}