n4nAI

Using Cohere embed-v4 with Node.js

Step-by-step guide to integrating Cohere embed-v4 with Node.js: install SDK, handle input types, batch requests, and verify embeddings in production.

n4n Team3 min read631 words

Audio narration

Coming soon — every post will get a voice note here.

Calling cohere embed-v4 node.js from a backend service is mostly about respecting the model’s input contract and building a client that survives rate limits. The API is small, but the defaults in most SDKs will bite you in production if you blindly loop over a corpus. This guide walks through a complete integration: auth, correct request shaping, batching, and a verification step you can run in a test.

Step 1: Provision credentials and install the client

Create a Cohere account and export an API key. Store it in your environment, never in source.

export COHERE_API_KEY="co-xxxxxxxxxxxxxxxx"
npm install cohere-ai

If you prefer zero dependencies, fetch is fine—Cohere’s endpoint is a standard JSON POST. But the official SDK gives you typed responses and spares you from hand-rolling error parsing.

Step 2: Initialize the Cohere client in Node.js

Instantiate once per process. Reuse the instance; the client is stateless aside from the token.

import { CohereClient } from "cohere-ai";

const cohere = new CohereClient({
  token: process.env.COHERE_API_KEY!,
});

export { cohere };

If you are on Node 18+ and skip the SDK, the equivalent call is:

async function embedTexts(texts: string[], inputType: string) {
  const res = await fetch("https://api.cohere.com/v1/embed", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.COHERE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ model: "embed-v4", texts, input_type: inputType }),
  });
  if (!res.ok) throw new Error(`Cohere ${res.status}: ${await res.text()}`);
  return (await res.json()) as { embeddings: number[][] };
}

Step 3: Set input_type correctly for embed-v4

Cohere’s embedding models require an input_type field. This is not optional, and swapping it silently degrades retrieval quality.

Document vs query embeddings

Use search_document when embedding content that will be stored and later retrieved. Use search_query when embedding a user’s search string or prompt. Classification and clustering have their own types.

const docs = await cohere.embed({
  model: "embed-v4",
  texts: ["Order shipped to Berlin", "Refund processed for invoice #992"],
  input_type: "search_document",
});

const query = await cohere.embed({
  model: "embed-v4",
  texts: ["where is my order"],
  input_type: "search_query",
});

Mixing these up is the most common bug we see in cohere embed-v4 node.js integrations. The vectors live in the same space only when typed correctly.

Truncation and token limits

Embed-v4 truncates inputs beyond its context window. Do not assume the full text was encoded. If you need long-document recall, chunk first:

function chunk(text: string, maxChars = 2000): string[] {
  const out: string[] = [];
  for (let i = 0; i < text.length; i += maxChars) {
    out.push(text.slice(i, i + maxChars));
  }
  return out;
}

Step 4: Make your first embedding call

A minimal end-to-end call with the SDK looks like this:

import { cohere } from "./client";

async function main() {
  const res = await cohere.embed({
    model: "embed-v4",
    texts: ["Vector databases index latent space, not keywords."],
    input_type: "search_document",
  });

  const vector = res.embeddings[0];
  console.log(`dim=${vector.length} sample=${vector.slice(0, 3)}`);
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});

The response shape is:

{
  "embeddings": [[0.012, -0.044, 0.119, "..."]],
  "meta": { "api_version": { "version": "1" } }
}

Log vector.length once at startup. If it changes across model versions, your vector column will reject inserts.

Step 5: Persist embeddings for retrieval

Assume PostgreSQL with pgvector. Create a table with a vector column sized to your model’s output.

CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
  id serial PRIMARY KEY,
  content text,
  embedding vector(1024)  -- replace 1024 with the dim you logged
);

Insert from Node:

import { Pool } from "pg";
const pool = new Pool();

async function store(text: string, embedding: number[]) {
  await pool.query(
    "INSERT INTO chunks (content, embedding) VALUES ($1, $2)",
    [text, `[${embedding.join(",")}]`]
  );
}

If you are building cohere embed-v4 node.js retrieval, keep the input_type used at write time consistent with the column’s purpose.

Step 6: Batch requests without melting the rate limit

Cohere rate limits are per-second and per-minute. Naive Promise.all over 10k rows gets you a 429 fast.

Concurrency control with p-limit

npm install p-limit
import pLimit from "p-limit";
const limit = pLimit(5); // max 5 concurrent embed calls

async function embedBatch(texts: string[]) {
  return Promise.all(
    texts.map((t) =>
      limit(() =>
        cohere.embed({ model: "embed-v4", texts: [t], input_type: "search_document" })
      )
    )
  );
}

Streaming large corpora

For big jobs, page from your source and embed in chunks of 100. The API accepts arrays, so batch 100 texts per call rather than one per call—but stay under the token ceiling.

async function embedCorpus(rows: { id: number; text: string }[]) {
  for (let i = 0; i < rows.length; i += 100) {
    const slice = rows.slice(i, i + 100);
    const res = await cohere.embed({
      model: "embed-v4",
      texts: slice.map((r) => r.text),
      input_type: "search_document",
    });
    await Promise.all(
      slice.map((r, idx) => store(r.text, res.embeddings[idx]))
    );
  }
}

Step 7: Verify the pipeline works

A integration test should assert that a known query ranks its own document above noise.

Cosine similarity smoke test

function cosine(a: number[], b: number[]): number {
  let dot = 0, na = 0, nb = 0;
  for (let i = 0; i < a.length; i++) {
    dot += a[i] * b[i];
    na += a[i] * a[i];
    nb += b[i] * b[i];
  }
  return dot / (Math.sqrt(na) * Math.sqrt(nb));
}

async function verify() {
  const doc = await cohere.embed({ model: "embed-v4", texts: ["kafka consumer lag alert"], input_type: "search_document" });
  const q = await cohere.embed({ model: "embed-v4", texts: ["alert on kafka lag"], input_type: "search_query" });
  const noise = await cohere.embed({ model: "embed-v4", texts: ["banana bread recipe"], input_type: "search_document" });

  const simGood = cosine(doc.embeddings[0], q.embeddings[0]);
  const simBad = cosine(doc.embeddings[0], noise.embeddings[0]);
  console.assert(simGood > simBad, "retrieval ordering broken");
  console.log({ simGood, simBad });
}

If simGood is not clearly higher, check input_type first, then chunking.

Logging dimensions and latency

Wrap the call:

const start = Date.now();
const res = await cohere.embed({ model: "embed-v4", texts, input_type });
console.log(`embed ${texts.length} in ${Date.now() - start}ms dim=${res.embeddings[0].length}`);

This catches model swaps and regression in p95 latency before they hit users.

Gotchas when shipping cohere embed-v4 node.js to production

The SDK retries on 429 by default with a fixed delay. Replace it with exponential backoff and a hard cap, or you will pile up hung promises during provider incidents.

Never embed user input as search_document and vice versa. The vectors are not interchangeable despite sharing a dimension.

If you later route through a gateway that normalizes multiple providers, keep your input_type mapping explicit—some OpenAI-compatible endpoints ignore it, but Cohere requires it. When fronting calls with a single OpenAI-compatible endpoint that addresses 240+ models, you lose native field validation, so assert the request shape in your own wrapper.

Finally, pin the model string. Writing model: "embed-v4" as a literal across files makes a future migration painful. Put it in one config module and inject it.

That is the full path from npm install to a verified retrieval pipeline with cohere embed-v4 node.js. The code blocks are runnable against the current API; adjust the vector dimension in your schema after logging the first response.

Tagscohereembeddingsnodejsapi-integration

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All embeddings api integration across languages posts →