Skip to main content
The @cloro-dev/cloro npm package is the official TypeScript/Node SDK for the cloro API — one typed client for Google Search and every AI answer engine. Each call is a single authenticated request that returns structured JSON with sources; there are no proxies, headless browsers, or selectors to maintain. It ships ESM and CommonJS builds with bundled type declarations. The source lives in the cloro-node repository. This page is the SDK reference. For a guided walkthrough with recipes, cost math, and production patterns, see the JavaScript & Node API guide. Prefer raw HTTP? Every method below maps to one endpoint you can call directly — see Making requests.

Prerequisites

Install the package

npm install @cloro-dev/cloro

Configure your API key

Set the key as an environment variable — the client reads CLORO_API_KEY automatically:
export CLORO_API_KEY="YOUR_API_KEY"
Or pass it directly when constructing the client:
import { Cloro } from "@cloro-dev/cloro";

const client = new Cloro({ apiKey: "YOUR_API_KEY" });

Quickstart

import { Cloro } from "@cloro-dev/cloro";

const client = new Cloro(); // reads CLORO_API_KEY

const res = await client.monitor.chatgpt({
  prompt: "What do you know about Acme Corp?",
  country: "US",
  include: { markdown: true },
});

console.log(res.result.text);
for (const source of res.result.sources) {
  console.log(source.position, source.url, source.label);
}
Every call resolves with the { success, result } envelope. Pass include: {...} to request extra formats (markdown, html, searchQueries, shopping, and more, depending on the engine).

Every engine, one client

AI engines take a prompt; Google Search and Google News take a query. Each method is a thin wrapper over one endpoint.
MethodEndpoint
client.monitor.google({ query, country })POST /v1/monitor/google
client.monitor.chatgpt({ prompt, country })POST /v1/monitor/chatgpt
client.monitor.gemini({ prompt, country })POST /v1/monitor/gemini
client.monitor.perplexity({ prompt, country })POST /v1/monitor/perplexity
client.monitor.copilot({ prompt, country })POST /v1/monitor/copilot
client.monitor.grok({ prompt, country })POST /v1/monitor/grok
client.monitor.aimode({ prompt, country })POST /v1/monitor/aimode
client.monitor.googleNews({ query, country })POST /v1/monitor/google/news
client.monitor.google also accepts location, uule, device, and pages (1–20). The client exposes client.countries() and client.states() for the supported countries and states.
client.monitor.grok is available, but the provider is temporarily unavailable — calls to it will fail until Grok access is restored.

Async task queue

For large batches, don’t loop synchronous calls — enqueue tasks and poll them. createBatch submits up to 500 tasks in one request; wait polls a task to completion with interval backoff. See Async requests.
import { Cloro } from "@cloro-dev/cloro";

const client = new Cloro();

const KEYWORDS = ["best running shoes", "trail running shoes", "waterproof running shoes"];

// Enqueue up to 500 tasks in a single call.
const results = await client.asyncTasks.createBatch(
  KEYWORDS.map((kw) => ({ taskType: "GOOGLE", payload: { query: kw, country: "US" } })),
);

for (const item of results) {
  if (item.success) {
    const done = await client.asyncTasks.wait(item.task.id);
    const organic = done.response.result.organicResults;
    if (organic?.length) {
      console.log(item.task.id, "→", organic[0].link);
    } else {
      console.log(item.task.id, "→ no results");
    }
  } else {
    console.log("failed:", item.error.message);
  }
}
For a single task, client.asyncTasks.run({ taskType, payload }) creates it and resolves once it completes. Valid taskType values: CHATGPT, GEMINI, PERPLEXITY, COPILOT, GROK, AIMODE, GOOGLE, GOOGLE_NEWS.

Reliability and configuration

The client retries timeouts, connection errors, and 429/5xx responses with exponential backoff — tune it with new Cloro({ maxRetries: 2, timeout: 60000 }) (timeout in milliseconds). Cap your own concurrency to your plan’s limit (see Concurrency), read the key from CLORO_API_KEY rather than hardcoding it, and access response fields with optional chaining (?.) since shapes vary by query (a Google result with no AI Overview omits aioverview).

Error handling

Every error subclasses CloroError, so one instanceof CloroError check catches everything. HTTP failures map to status-specific types:
import { Cloro, AuthenticationError, RateLimitError, CloroError } from "@cloro-dev/cloro";

const client = new Cloro();

try {
  const res = await client.monitor.chatgpt({ prompt: "...", country: "US" });
} catch (err) {
  if (err instanceof AuthenticationError) {
    // 401 — bad or missing API key
  } else if (err instanceof RateLimitError) {
    // 429 — the client already retried; back off further if it persists
  } else if (err instanceof CloroError) {
    // everything else
  } else {
    throw err;
  }
}
ExceptionMeaning
AuthenticationError401 — missing or invalid API key
BadRequestError400 — malformed request or failed validation
PermissionDeniedError403 — key not allowed for this action
NotFoundError404 — resource does not exist
ConflictError409 — conflicts with current state (e.g. concurrency limit)
RateLimitError429 — too many requests
InternalServerError5xx — the API failed to process the request
APITimeoutErrorrequest timed out before a response
TaskFailedError / TaskTimeoutErrorasync task failed, or did not finish within the poll timeout

Next steps