Treat AI visibility like any other quality gate: scan the build on every deploy, and fail the pipeline if the score falls below your threshold. Catch a missing canonical, broken FAQ schema, or a regressed SSR render before it ships.
Open Settings → API keys, click New API key, give it a label like ci-deploy, and copy the aeol_… token shown once. Add it as a secret in your CI provider (GitHub Actions: AEOLENS_API_KEY; GitLab: a masked variable).
API access ships on Growth ($99/mo) and Agency ($249/mo) plans.
The endpoint runs synchronously: it queues the scan, waits up to max_wait_seconds, and returns a pass/fail body. Run it against the URL you just deployed (a staging URL is the common pattern).
curl -X POST https://aeolens.ai/api/v1/ci/scan \
-H "Authorization: Bearer $AEOLENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://staging.acme.com",
"min_score": 70,
"max_wait_seconds": 180
}'Response (status 200):
{
"status": "complete",
"passed": true,
"score": 78,
"threshold": 70,
"scan_id": "0a1b2c3d-…",
"scan_url": "https://aeolens.ai/scan/0a1b2c3d-…",
"failed_checks": ["SCHEMA", "OG"],
"error": null,
"wait_seconds": 12.4
}The endpoint always returns 200 on a successful scan — even when the threshold isn't met. Your script reads .passedand decides whether to fail the build. This keeps "AEOlens errored" distinguishable from "deploy was blocked."
Drop this into .github/workflows/aeo.yml (or merge into your existing deploy workflow). It runs after deploy and fails the job if AEOlens reports passed: false.
name: AEO gate
on:
deployment_status:
states: [success]
jobs:
aeo-scan:
runs-on: ubuntu-latest
steps:
- name: Run AEOlens scan
env:
AEOLENS_API_KEY: ${{ secrets.AEOLENS_API_KEY }}
DEPLOY_URL: ${{ github.event.deployment_status.target_url }}
run: |
RESPONSE=$(curl -sf -X POST https://aeolens.ai/api/v1/ci/scan \
-H "Authorization: Bearer $AEOLENS_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"url\":\"$DEPLOY_URL\",\"min_score\":70}")
echo "$RESPONSE" | jq .
PASSED=$(echo "$RESPONSE" | jq -r .passed)
if [ "$PASSED" != "true" ]; then
echo "::error::AEO gate failed — see scan_url in the response."
exit 1
fiDrop into .gitlab-ci.yml. Set AEOLENS_API_KEY and DEPLOY_URL as project variables.
aeo_gate:
stage: post-deploy
image: curlimages/curl:latest
script:
- |
RESPONSE=$(curl -sf -X POST https://aeolens.ai/api/v1/ci/scan \
-H "Authorization: Bearer $AEOLENS_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"url\":\"$DEPLOY_URL\",\"min_score\":70}")
echo "$RESPONSE"
echo "$RESPONSE" | grep -q '"passed":true' || exit 1
rules:
- if: $CI_COMMIT_BRANCH == "main"| Field | Type | Default | Description |
|---|---|---|---|
| url | string | required | Public URL to scan. Private/loopback hosts are rejected. |
| min_score | int (0-100) | 70 | Threshold for .passed. Below this returns passed: false. |
| max_wait_seconds | int (10-300) | 180 | How long to wait synchronously. After this, returns status: "timeout". |
| status | passed | Meaning |
|---|---|---|
| complete | true / false | Scan finished. Check passed against your threshold. |
| timeout | false | Scan still running after max_wait_seconds. Use scan_id to poll later, or increase the budget. |
| failed | false | Scan errored (bad URL, network failure). See error field. |
The CI endpoint consumes a scan from your plan's monthly quota — it isn't a separate bucket. Growth covers unlimited scans with a fair-use cap of 15/day per workspace; Agency covers 50/day. If you scan on every PR you'll want Growth or above.
Hitting the cap returns a 402 with code: "QUOTA_EXCEEDED" rather than the 200 pass/fail shape.
Spin up an API key in 30 seconds and drop the snippet above into your pipeline.
Get API key