Scrape with JavaScript

Use native fetch in a modern Node.js environment to retrieve a page. No client SDK is required for this example.

Keep the credential on the server

Create a full-access key and set LUNI_API_KEY in the environment where your script or server runs. The example is for server-side JavaScript with native fetch and AbortSignal.timeout, not a script to paste into a public web page.

If you build a browser interface, send its request to your own server and call Luni from there. Validate what the browser is allowed to ask for and apply your own user-level budgets before passing work to the API.

Fetch and check the response

Save the example as a .mjs file and run it with a current Node.js version that provides native fetch. The request declares its JSON body with Content-Type, and the timeout bounds how long your client waits.

fetch does not reject merely because the server returns a 4xx or 5xx status. Check response.ok before reading the success payload. In a production application, retain the error status and operation context rather than exposing credentials or full request headers in logs.

Node.js request
const apiKey = process.env.LUNI_API_KEY;
if (!apiKey) throw new Error("Set LUNI_API_KEY first");

const response = await fetch("https://api.luni.gg/v1/scrape", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + apiKey,
    "Content-Type": "application/json",
    "Idempotency-Key": "node-first-page-001",
  },
  body: JSON.stringify({
    url: "https://example.com", formats: ["markdown"],
  }),
  signal: AbortSignal.timeout(60000),
});
if (!response.ok) throw new Error("HTTP " + response.status);
const result = await response.json();
console.log(result.data.markdown);

Choose a stable request identity

node-first-page-001 represents this particular scrape. Reuse it if a network interruption makes you uncertain whether the request completed. Do not create a fresh ID inside every retry attempt, because that would describe a new operation rather than a replay.

For a new page or a new observation, generate and persist a new logical ID. Using the same key with a different request body returns 409. Completed replays retrieve the saved result without another credit charge.

Handle a real collection

Begin with one page and inspect its Markdown and source metadata. When expanding to many pages, control concurrency in your own worker and handle rate-limit responses. Use a bounded crawl if you want an asynchronous job for a single website.

Do not assume an authenticated session is available. Luni retrieves public content over HTTP. Store successful results in your own application when you need durable retention beyond seven days.