Skip to main content
Beyond attention heatmaps and clarity scores, the EyeQuant API can generate natural-language summaries and recommendations for an analysis.
  • Summary — a concise description of what the analysis found.
  • Recommendations — concrete, actionable suggestions for improving the design.
Both features run asynchronously: trigger generation with a POST, then poll with a GET until the content is ready.
When generation completes, the content is returned in the data field as a Markdown string. Render it as Markdown to preserve headings and lists.

Which ID to use

This is the most common source of confusion, so it is worth getting right. POST /analyses returns the screenshot id. Use that id to poll Get an analysis. When the analysis succeeds, that response also returns _internal_exposed_id — the analysis id. Use the analysis id for:
1

Add context (optional)

Before triggering generation, you can attach context to the analysis so the generated content focuses on the page, audience, and business outcome that matter to you. See Update analysis metadata.
curl \
  -X PUT \
  -H "Authorization: Bearer $apikey" \
  -H "Content-Type: application/json" \
  -d '{ "description": "Homepage hero", "goal": "Increase sign-ups" }' \
  https://api.eyequant.com/v2/update-meta/screenshot/$analysisId
2

Trigger generation

Trigger a summary (use the recommendations path for recommendations). Calling POST again is safe — if generation was already triggered, it returns the current status.
curl \
  -X POST \
  -H "Authorization: Bearer $apikey" \
  https://api.eyequant.com/v2/analyses/summary/$analysisId
3

Poll for the result

Poll the GET endpoint every few seconds until status is completed (or failed, or a 410 Gone). When complete, the Markdown content is in data.
curl \
  -H "Authorization: Bearer $apikey" \
  -X GET \
  https://api.eyequant.com/v2/analyses/summary/$analysisId

JavaScript example

const baseUrl = 'https://api.eyequant.com/v2';

async function requestJson(path, options = {}) {
  const headers = {
    Authorization: `Bearer ${apiKey}`,
    ...options.headers,
  };

  if (options.body) {
    headers['Content-Type'] = 'application/json';
  }

  const response = await fetch(`${baseUrl}${path}`, { ...options, headers });
  const body = await response.json();

  if (!response.ok) {
    throw new Error(body.msg || body.error?.message || 'Request failed');
  }

  return body;
}

async function waitForGeneratedResult(path) {
  while (true) {
    const result = await requestJson(path);

    if (result.status === 'completed') {
      return result.data; // Markdown string
    }

    if (result.status === 'failed' || result.status === 'expired') {
      throw new Error(result.msg);
    }

    await new Promise((resolve) => setTimeout(resolve, 3000));
  }
}

await requestJson(`/update-meta/screenshot/${analysisId}`, {
  method: 'PUT',
  body: JSON.stringify({
    description: 'Homepage hero',
    goal: 'Increase sign-ups',
  }),
});

await requestJson(`/analyses/summary/${analysisId}`, { method: 'POST' });

const summaryMarkdown = await waitForGeneratedResult(
  `/analyses/summary/${analysisId}`
);
To generate recommendations instead, use /analyses/recommendations/${analysisId} for both the POST trigger and the polling path.

Result expiry

Summary and recommendation tasks become unavailable one hour after they were started, after which the request returns 410 Gone. If this happens, re-trigger generation with a POST and poll again.

Errors

The generated-content endpoints use a { "success": false, "msg": "..." } error shape:
  • 404 analysis_not_found — no analysis with that id for your account.
  • 404 summary_not_found (or recommendations_not_found) — nothing generated yet; trigger one with POST first.
  • 410 analysis_expired — the task is older than one hour.
  • 500 generation failed — re-trigger with POST.