Developer Guide: Leveraging Citations & Enrichment
Practical patterns for building features on top of the Citations and Sources Enrichment APIs
This guide walks through common integration patterns for the Explorer Citations and Sources Enrichment APIs. All examples use the public API with an API key.
Fetching and Enriching Citations End-to-End
The typical workflow is: fetch citations, then enrich the source URLs with category and brand data.
Fetch a page of citations
curl "https://api.getmint.ai/api/domains/YOUR_DOMAIN_ID/explorer/citations?\
page=1&limit=50&latestOnly=true" \
-H "X-API-Key: YOUR_API_KEY"Collect unique URLs from the response
Extract the link field from each citation row and deduplicate.
Enrich the URLs
curl -X POST "https://api.getmint.ai/api/domains/YOUR_DOMAIN_ID/sources/enrichment" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"urls": ["https://techcrunch.com/...", "https://g2.com/..."],
"reportId": "REPORT_ID_FROM_YOUR_CONTEXT"
}'Merge the data
Join the enrichment response back into the citation rows by URL key.
Full Code Examples
const API_KEY = 'YOUR_API_KEY';
const API_BASE = 'https://api.getmint.ai/api';
/**
* Fetch all citations for a domain, paginating automatically.
*/
async function fetchAllCitations(domainId, filters = {}) {
const citations = [];
let page = 1;
let hasNext = true;
while (hasNext) {
const params = new URLSearchParams({
page: String(page),
limit: '100',
...filters,
});
const res = await fetch(
`${API_BASE}/domains/${domainId}/explorer/citations?${params}`,
{ headers: { 'X-API-Key': API_KEY } }
);
const data = await res.json();
citations.push(...data.data);
hasNext = data.hasNext;
page++;
}
return citations;
}
/**
* Enrich a batch of URLs with category + brand mention data.
* Automatically chunks into batches of 100.
*/
async function enrichUrls(domainId, urls, reportId, topicId) {
const enrichment = {};
for (let i = 0; i < urls.length; i += 100) {
const batch = urls.slice(i, i + 100);
const res = await fetch(
`${API_BASE}/domains/${domainId}/sources/enrichment`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
},
body: JSON.stringify({ urls: batch, reportId, topicId }),
}
);
Object.assign(enrichment, await res.json());
}
return enrichment;
}
/**
* Combined: fetch citations + enrich their sources.
*/
async function getCitationsWithEnrichment(domainId, reportId) {
// 1. Fetch citations
const citations = await fetchAllCitations(domainId, { latestOnly: 'true' });
// 2. Collect unique URLs
const urls = [...new Set(
citations.map((c) => c.link).filter(Boolean)
)];
// 3. Enrich
const enrichment = await enrichUrls(domainId, urls, reportId);
// 4. Merge
return citations.map((citation) => ({
...citation,
enrichment: citation.link ? enrichment[citation.link] : undefined,
}));
}
// Usage
const results = await getCitationsWithEnrichment('your-domain-id', 'your-report-id');
for (const row of results) {
console.log(`[${row.promptCategory}] ${row.source}`);
if (row.enrichment?.sourceCategory) {
console.log(` Category: ${row.enrichment.sourceCategory}`);
}
if (row.enrichment?.detectedBrands) {
for (const brand of row.enrichment.detectedBrands) {
const label = brand.isBrand ? 'own' : 'competitor';
console.log(` Brand: ${brand.name} (${label}, ${brand.count} mentions)`);
}
}
}import requests
API_KEY = 'YOUR_API_KEY'
API_BASE = 'https://api.getmint.ai/api'
HEADERS = {'Content-Type': 'application/json', 'X-API-Key': API_KEY}
def fetch_all_citations(domain_id: str, filters: dict = None) -> list:
"""Fetch all citations for a domain, paginating automatically."""
citations = []
page = 1
has_next = True
while has_next:
params = {'page': page, 'limit': 100, **(filters or {})}
res = requests.get(
f'{API_BASE}/domains/{domain_id}/explorer/citations',
headers=HEADERS,
params=params,
)
data = res.json()
citations.extend(data['data'])
has_next = data['hasNext']
page += 1
return citations
def enrich_urls(domain_id: str, urls: list, report_id: str, topic_id: str = None) -> dict:
"""Enrich URLs in batches of 100."""
enrichment = {}
for i in range(0, len(urls), 100):
batch = urls[i:i + 100]
body = {'urls': batch, 'reportId': report_id}
if topic_id:
body['topicId'] = topic_id
res = requests.post(
f'{API_BASE}/domains/{domain_id}/sources/enrichment',
headers=HEADERS,
json=body,
)
enrichment.update(res.json())
return enrichment
def get_citations_with_enrichment(domain_id: str, report_id: str) -> list:
"""Fetch citations and enrich their sources."""
# 1. Fetch
citations = fetch_all_citations(domain_id, {'latestOnly': 'true'})
# 2. Collect unique URLs
urls = list({c['link'] for c in citations if c.get('link')})
# 3. Enrich
enrichment = enrich_urls(domain_id, urls, report_id)
# 4. Merge
for citation in citations:
citation['enrichment'] = enrichment.get(citation.get('link'))
return citations
# Usage
results = get_citations_with_enrichment('your-domain-id', 'your-report-id')
for row in results:
print(f"[{row['promptCategory']}] {row['source']}")
e = row.get('enrichment')
if e and e.get('sourceCategory'):
print(f" Category: {e['sourceCategory']}")
if e and e.get('detectedBrands'):
for brand in e['detectedBrands']:
label = 'own' if brand['isBrand'] else 'competitor'
print(f" Brand: {brand['name']} ({label}, {brand['count']} mentions)")Common Patterns
Filter by Visibility Citations Only
To focus on citations from visibility analysis (where your brand was or wasn't mentioned by LLMs):
curl "https://api.getmint.ai/api/domains/YOUR_DOMAIN_ID/explorer/citations?\
promptCategory=visibility&latestOnly=true&limit=100" \
-H "X-API-Key: YOUR_API_KEY"Find Which Sources Mention Your Competitors
Enrich your citation URLs and filter for entries where detectedBrands contains items with isBrand: false:
const enriched = await getCitationsWithEnrichment(domainId, reportId);
const competitorSources = enriched.filter((row) =>
row.enrichment?.detectedBrands?.some((b) => !b.isBrand)
);
// Group by competitor name
const byCompetitor = {};
for (const row of competitorSources) {
for (const brand of row.enrichment.detectedBrands.filter((b) => !b.isBrand)) {
byCompetitor[brand.name] ??= [];
byCompetitor[brand.name].push({
url: row.link,
domain: row.linkDomain,
mentions: brand.count,
});
}
}Export Citations to CSV
Fetch all pages and flatten into a CSV-friendly structure:
const allCitations = await fetchAllCitations(domainId, {
startDate: '2025-01-01',
endDate: '2025-03-01',
});
const csvRows = allCitations.map((c) => ({
prompt: c.promptText ?? '',
category: c.promptCategory,
source: c.source,
url: c.link ?? '',
domain: c.linkDomain,
model: c.model,
topic: c.topicName ?? '',
fanOutQueries: c.fanOutQueries.join('; '),
}));Build a Source Category Breakdown
Use enrichment data to see which categories your citations come from:
const enrichment = await enrichUrls(domainId, urls, reportId);
const categoryCounts = {};
for (const [url, data] of Object.entries(enrichment)) {
if (data.sourceCategory) {
categoryCounts[data.sourceCategory] ??= 0;
categoryCounts[data.sourceCategory]++;
}
}
// Sort by frequency
const sorted = Object.entries(categoryCounts)
.sort(([, a], [, b]) => b - a);
console.log('Top source categories:');
for (const [category, count] of sorted.slice(0, 10)) {
console.log(` ${category}: ${count} citations`);
}Rate Limiting Considerations
The Sources Enrichment endpoint accepts up to 100 URLs per request. If you have more URLs, batch them into chunks of 100 as shown in the examples above.
- Both endpoints share the default rate limit of 200 requests per minute per API key.
- The Citations endpoint is cached for 60 seconds — repeated identical requests within that window are free.
- When paginating through large citation sets, add a small delay between pages if you're close to the rate limit.
See Rate Limits for retry strategies and backoff patterns.
Next Steps
- Citations & Sources Enrichment Reference — full parameter reference and error codes
- Explorer Citations — Interactive API — try requests in the Swagger UI
- Sources Enrichment — Interactive API — try requests in the Swagger UI
- Mini Audit API — generate the reports that produce citations