Embeddings
POST /v1/embeddings turns clinical text into embedding vectors with ClinEmbed-1. It follows the OpenAI embeddings API shape plus one field, input_type, so the openai SDKs work when you point their base URL at the gateway. For the base URLs and the headers every response carries, see API overview.
POST /v1/embeddingsAuthorization: Bearer fh_...Content-Type: application/jsonRequest
Section titled “Request”Headers
Section titled “Headers”The request takes the following headers:
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer followed by your API key. |
Content-Type |
Yes | application/json. |
x-request-id |
No | Your own ID for the request. The gateway returns it in the x-request-id response header; without it, the gateway generates a UUID. |
The body is a JSON object with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | The model ID. ClinEmbed-1 is the only model served. A request for a model your organization doesn’t have access to returns 403 model_not_enabled. |
input |
string or array of strings | Yes | The text to embed. An array holds 1 to 1,000 strings and returns one embedding per string, in the same order. Each string can have at most 512 tokens; see Long inputs. |
input_type |
string | No | query or document. Tells the model which side of a retrieval pair the text is on; see Input types. |
encoding_format |
string | No | float (default) or base64. See Encoding formats. |
truncate_prompt_tokens |
integer | No | Set to -1 to truncate inputs longer than 512 tokens instead of rejecting them. See Long inputs. |
dimensions |
integer | No | Not supported. ClinEmbed-1 always returns 1024 dimensions; a request that sets dimensions fails with 400 invalid_request. |
user |
string | No | Accepted and forwarded for compatibility with the OpenAI embeddings API. |
The body must be at most 1 MiB. The gateway drops fields that aren’t listed here before the request reaches the model.
Input types
Section titled “Input types”ClinEmbed-1 embeds search queries differently from documents, so that a query’s vector is close to the vectors of the documents that answer it. input_type tells the model which one a text is:
query: a search string. The model prepends its retrieval instruction to the text before embedding it, so the vector differs from the one you get without aninput_type.document: text that you index for retrieval. This produces the same vector as omittinginput_type.
For retrieval and semantic search, embed your corpus with document and every search string with query, and then rank documents by similarity to the query vector. For tasks where all texts play the same role, such as clustering or deduplicating notes, embed them all the same way: with document or with no input_type.
The gateway rejects any other value with 400 invalid_body.
Long inputs
Section titled “Long inputs”ClinEmbed-1 accepts up to 512 tokens per input string. The count includes the instruction that input_type: "query" adds. By default, one input over the limit fails the whole request with 400 invalid_request and a detail that mentions the maximum context length. To embed the first 512 tokens of each over-long input instead, set truncate_prompt_tokens to -1. The response’s usage then counts only the tokens the model embedded.
Encoding formats
Section titled “Encoding formats”encoding_format selects how the response encodes each vector:
float(default):embeddingis a JSON array of 1024 numbers.base64:embeddingis a base64 string of the 1024 values as little-endian 32-bit floats, which makes the response smaller. Decode it withstruct.unpack("<1024f", base64.b64decode(value))in Python, or decode the base64 into bytes and read them as aFloat32Arrayin JavaScript.
The openai SDKs request base64 when you don’t set encoding_format and decode it for you, so embedding is an array of numbers in your code either way.
Response
Section titled “Response”A success is 200 with Content-Type: application/json; charset=utf-8 and the following fields:
| Field | Type | Description |
|---|---|---|
id |
string | An ID for the response. |
object |
string | Always list. |
created |
integer | When the response was created, as a Unix timestamp in seconds, as in the OpenAI API. |
model |
string | The model that produced the embeddings: ClinEmbed-1. |
data |
array | One item per input string, in input order. |
data[].object |
string | Always embedding. |
data[].index |
integer | The position of the input this embedding belongs to, starting at 0. |
data[].embedding |
array of numbers, or string | The 1024-dimensional vector, as numbers or as a base64 string depending on encoding_format. Vectors are unit length, so the dot product of two embeddings is their cosine similarity. |
usage.prompt_tokens |
integer | Tokens in the inputs, including the instruction that input_type: "query" adds. |
usage.total_tokens |
integer | Same as prompt_tokens. The gateway records this number as your usage for the request. |
The following example shows a response for two inputs:
{ "id": "embd-3f9c2a7e", "object": "list", "created": 1758643200, "model": "ClinEmbed-1", "data": [ { "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, 0.0078 /* 1024 values */] }, { "object": "embedding", "index": 1, "embedding": [-0.0311, 0.0197, 0.0402 /* 1024 values */] } ], "usage": { "prompt_tokens": 48, "total_tokens": 48 }}Examples
Section titled “Examples”The examples embed two clinical notes as documents, and then embed a search string as a query and rank the notes against it. Vectors are unit length, so ranking by dot product ranks by cosine similarity. The SDK examples set the base URL with a /v1 suffix and pass the Fourier API key as the API key. Because input_type isn’t in the SDK types, the Python example passes it through extra_body, and the TypeScript example widens the parameter type.
Embed documents
Section titled “Embed documents”curl https://gateway.fourierhealth.com/v1/embeddings \ -H "Authorization: Bearer $FOURIER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ClinEmbed-1", "input": [ "Hemoglobin A1c 7.9% (ref 4.0-5.6) collected 03/14/2024.", "Allergies: penicillin (rash), sulfa drugs. No known food allergies." ], "input_type": "document" }'import os
from openai import OpenAI
client = OpenAI( api_key=os.environ["FOURIER_API_KEY"], base_url="https://gateway.fourierhealth.com/v1",)
documents = [ "Hemoglobin A1c 7.9% (ref 4.0-5.6) collected 03/14/2024.", "Allergies: penicillin (rash), sulfa drugs. No known food allergies.",]response = client.embeddings.create( model="ClinEmbed-1", input=documents, extra_body={"input_type": "document"},)document_vectors = [item.embedding for item in response.data]print(len(document_vectors), len(document_vectors[0])) # 2 1024import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.FOURIER_API_KEY, baseURL: "https://gateway.fourierhealth.com/v1",});
type EmbedParams = OpenAI.EmbeddingCreateParams & { input_type?: "query" | "document" };
const documents = [ "Hemoglobin A1c 7.9% (ref 4.0-5.6) collected 03/14/2024.", "Allergies: penicillin (rash), sulfa drugs. No known food allergies.",];const params: EmbedParams = { model: "ClinEmbed-1", input: documents, input_type: "document" };const response = await client.embeddings.create(params);const documentVectors = response.data.map((item) => item.embedding);console.log(documentVectors.length, documentVectors[0]?.length); // 2 1024Embed a query and rank the documents
Section titled “Embed a query and rank the documents”This example continues from the previous one. The HbA1c note scores highest.
curl https://gateway.fourierhealth.com/v1/embeddings \ -H "Authorization: Bearer $FOURIER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ClinEmbed-1", "input": "most recent HbA1c result", "input_type": "query" }'response = client.embeddings.create( model="ClinEmbed-1", input="most recent HbA1c result", extra_body={"input_type": "query"},)query_vector = response.data[0].embedding
scores = [sum(q * d for q, d in zip(query_vector, vector)) for vector in document_vectors]print(documents[scores.index(max(scores))])const query: EmbedParams = { model: "ClinEmbed-1", input: "most recent HbA1c result", input_type: "query",};const queryResponse = await client.embeddings.create(query);const queryVector = queryResponse.data[0]?.embedding ?? [];
const dot = (a: number[], b: number[]) => a.reduce((sum, value, i) => sum + value * (b[i] ?? 0), 0);const scores = documentVectors.map((vector) => dot(queryVector, vector));console.log(documents[scores.indexOf(Math.max(...scores))]);Errors
Section titled “Errors”Besides the authentication, rate limit, and availability errors every endpoint can return, this endpoint returns the following:
| Status | Code | When |
|---|---|---|
400 |
invalid_body |
The body fails validation: model missing, input empty or over 1,000 strings, or an unknown input_type or encoding_format. detail names the field. |
400 |
invalid_request |
The model rejected the request: an input over 512 tokens without truncate_prompt_tokens, or a dimensions value. detail carries the model’s message. |
403 |
model_not_enabled |
Your organization doesn’t have access to the requested model, or no such model exists. Contact Fourier Health to request access. |
For the response format and the full list of codes, see Errors.