How it works
Most AI crawlers download your page and read the HTML, without running any JavaScript. Today, that HTML contains only the empty widget boxes. With dual rendering, your server fills each box with the citations before it sends the page:
<div id="bizgenius-citation-widget-container-list"
data-maxnum="10"></div><div id="bizgenius-citation-widget-container-list"
data-maxnum="10">
<div class="bizgenius-ssr" ...>
Regional brain volume abnormalities in
Lesch-Nyhan disease ... The Lancet Neurology,
2013. doi:10.1016/s1474-4422(13)70238-2 ...It's called dual rendering because both happen: your server prints the citations, then the widget takes over in the browser for search, filters and paging. Visitors see the same widget as before, and the citations show up about a second sooner. Every visitor, person or crawler, gets the same page.
See it live: open our demo page, then View Page Source. The paper titles are in the HTML.
Choose your setup
Which one fits depends on how your website is built. If you're not sure, ask whoever looks after your website, or ask us and we'll check.
- The widget must already be on your pages: the
citation.min.jsscript and thebizgenius-citation-widget-container-...boxes. - The boxes must be in the page HTML and empty. A box added by Google Tag Manager or other JavaScript, or one with anything inside (even "Loading ..." or a comment), is left alone. Move such boxes into your page or theme template.
- Testing on a staging site? Tell us its domain so we can allow it. Until then its boxes simply stay empty.
1. WordPress
Our plugin, BizGenius Citations for AI Search, finds the empty widget boxes on each page and fills them with your citations. It works with any theme and page builder (Elementor, WPBakery, Avada, Gutenberg, classic editor).
- Download the plugin.
bizgenius-ssr.zip version 1.0.7, 8 KB - Install it. In WordPress admin go to Plugins → Add New → Upload Plugin, choose
bizgenius-ssr.zip, then click Install Now. - Click Activate. That's it. There are no settings to fill in.
- Clear your page cache once if you use one (WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Engine, Cloudflare). Cached pages are then saved with the citations in them.
Requirements: WordPress 5.0 or later and PHP 7.2 or later.
To remove it, deactivate and delete the plugin. Your pages go back to browser-only rendering.
2. Other PHP websites
For any website whose pages are built by PHP, such as Magento, TYPO3, Drupal, Joomla or your own code. One file,
bizgenius-ssr.php, does the same job as the WordPress plugin. Keep your widget boxes and the
citation.min.js script as they are.
- Download the file.
bizgenius-ssr.php 20 KB - Put it on your server, preferably outside the web root, for example
/var/www/bizgenius-ssr.php. - Load it on each page that has widget boxes. Make this the very first line of the page, before any HTML:
<?php require_once '/var/www/bizgenius-ssr.php'; ?>Many pages? Load it for the whole site instead, and skip editing them. PHP has a setting,auto_prepend_file, that runs a file before every page. Set it in one of these places (one is enough):- Most servers: add this line to the file
.user.iniin your website's root folder (create the file if it isn't there; on Magento 2 the root folder ispub/). PHP picks it up within 5 minutes.auto_prepend_file = "/var/www/bizgenius-ssr.php" - Apache running PHP as a module (mod_php), which ignores
.user.ini: add this line to the.htaccessfile in your root folder instead. Use it only on mod_php: on other servers it makes the site show an error.php_value auto_prepend_file "/var/www/bizgenius-ssr.php" - The whole server: your server administrator sets
auto_prepend_fileinphp.ini(or the PHP-FPM pool file) and restarts PHP.
require_onceline are fine: the file only runs once. - Most servers: add this line to the file
- Clear your full-page cache once if you use one (Varnish, Fastly, Magento's built-in cache, a CDN). Cached pages are then saved with the citations in them.
Requirements: PHP 7.2 or later, with the curl extension (or allow_url_fopen). Only HTML pages are touched; images,
JSON and downloads pass straight through.
Optional settings
Define these in any config file your pages already load, or uncomment the lines at the top of bizgenius-ssr.php:
define('BIZGENIUS_SSR_KEY', 'YOUR_WIDGET_KEY'); // only if your widget key isn't in the page HTML
define('BIZGENIUS_SSR_CACHE_DIR', '/path/to/cache'); // a writable folder, instead of the system temp folder
define('BIZGENIUS_SSR_DISABLE', true); // switch it off$html = bizgenius_ssr_fill($html);3. Any other website: our feed (HTML or JSON)
If your website doesn't run PHP (Next.js, Nuxt, Gatsby, Astro, Adobe Experience Manager, Java, .NET, Python, Ruby ...), your developer calls one URL from your server, or at build time, and prints the result into the page. The feed comes in two formats:
| Ready-made HTML | Data (JSON) | |
|---|---|---|
| Looks like | Your widget, exactly | Your own design |
| Where it goes | Inside the empty widget box, printed as it is | Anywhere, in your own template |
| Widget script (JS) | Required: paper titles open through it, and it adds search and paging | Optional |
| Your work | Print one piece of HTML | Build a template and escape the text |
Already showing the widget? Use ready-made HTML. Want the citations in your own design? Use data (JSON).
The request
GET https://www.bizgeniusapp.com/api/render.php with these parameters:
| Parameter | What to send |
|---|---|
key | Required. Your widget key: the content of your
bizgeniusapp-key meta tag, or the key= in your citation.min.js URL. Use 1
for demo data. |
domain | Required. Your website's domain, e.g. www.example.com. It must
be a domain registered for your widget, even when you fetch at build time. |
part | Required. Which box: list, list-top,
count-total-year-month, aisummary and so on, listed in the table under
Data (JSON). form (the search form) is HTML only. |
format | Leave it out for ready-made HTML; json for data. |
a[...] | Optional. The same settings as the box's data-* attributes, without
data-: a[product-name]=180, a[maxnum]=10, a[order]=publicationDate,
a[show-abstract]=yes and so on. See the Widget Playground for every setting. |
sourcekeyword | Optional. Only if your citation.min.js URL has a
sourcekeyword=... (one account shared by several brand websites): send the same value, or you get every
brand's papers. |
start | Optional, data (JSON) only. Paging for lists: 0, then 10,
20 ... |
products[] | Optional, part=count-product only: up to 100 product names in one call, for
listing pages. |
Try it with demo data: 2 citations (HTML) · 2 citations (JSON) · citation counts (JSON)
Ready-made HTML
Leave out format, and you get the HTML the widget shows. Print it, exactly as it is, inside the matching empty widget
box. Its styles only apply inside that box, so keep the box, with the same data-* settings as your
a[...], and the citation.min.js script on your page. The widget then takes over in the browser as usual.
// Server side or at build time: a Next.js server component, Nuxt server route,
// Gatsby / Astro build, Cloudflare Worker, Express ...
const params = new URLSearchParams({
key: 'YOUR_WIDGET_KEY',
domain: 'www.example.com', // your website's domain
part: 'list',
'a[maxnum]': '10', // the same settings as the box's data-* attributes
'a[order]': 'publicationDate',
});
let html = '';
try {
const res = await fetch('https://www.bizgeniusapp.com/api/render.php?' + params, {
signal: AbortSignal.timeout(3000), // don't hold up your page
// Next.js: next: { revalidate: 43200 }, // keep a copy for 12 hours
});
if (res.status === 200) html = await res.text(); // 204 = nothing to show
} catch (e) { /* BizGenius slow or unreachable: leave the box empty */ }
// Print it as it is, inside the empty box, e.g. in JSX:
// <div id="bizgenius-citation-widget-container-list" data-maxnum="10" data-order="publicationDate"
// dangerouslySetInnerHTML={{ __html: html }} />import requests
html = ""
try:
r = requests.get("https://www.bizgeniusapp.com/api/render.php", params={
"key": "YOUR_WIDGET_KEY",
"domain": "www.example.com", # your website's domain
"part": "list",
"a[maxnum]": "10", # the same settings as the box's data-* attributes
"a[order]": "publicationDate",
}, timeout=3)
if r.status_code == 200: # 204 = nothing to show
html = r.text
except requests.RequestException:
pass # leave the box empty
# Print it as it is, inside the empty box (Jinja2 / Django: {{ html|safe }}):
# <div id="bizgenius-citation-widget-container-list" data-maxnum="10" data-order="publicationDate">{{ html|safe }}</div><?php
$html = @file_get_contents('https://www.bizgeniusapp.com/api/render.php?' . http_build_query([
'key' => 'YOUR_WIDGET_KEY',
'domain' => 'www.example.com', // your website's domain
'part' => 'list',
'a' => ['maxnum' => '10', 'order' => 'publicationDate'], // the box's data-* settings
]), false, stream_context_create(['http' => ['timeout' => 3]])); // '' on 204, false on failure
?>
<div id="bizgenius-citation-widget-container-list" data-maxnum="10" data-order="publicationDate"><?= $html ?: '' ?></div>curl -G "https://www.bizgeniusapp.com/api/render.php" \
--data-urlencode "key=YOUR_WIDGET_KEY" \
--data-urlencode "domain=www.example.com" \
--data-urlencode "part=list" \
--data-urlencode "a[maxnum]=10"A citation list (part=list) comes back like this, shortened. The ld+json part is schema.org data that
tells search engines and AI assistants what each paper is:
<div class="bizgenius-ssr" data-ssr-part="list" style="display:contents">
<div class="bizgenius-citation-list" style="width:100%;text-align:left;">
<style> ... </style>
... one card per paper: title, authors, journal, year, DOI, snippet ...
</div>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"ItemList",
"name":"Peer-reviewed publications citing our products", ...}</script>
</div>Data (JSON)
Add format=json to the same request and show the results with your own template.
// Server side or at build time: a Next.js server component, Nuxt server route,
// Gatsby / Astro build, Cloudflare Worker, Express ...
const params = new URLSearchParams({
key: 'YOUR_WIDGET_KEY',
domain: 'www.example.com', // your website's domain
part: 'list',
format: 'json',
'a[maxnum]': '10',
'a[order]': 'publicationDate',
});
let papers = [];
try {
const res = await fetch('https://www.bizgeniusapp.com/api/render.php?' + params, {
signal: AbortSignal.timeout(3000), // don't hold up your page
// Next.js: next: { revalidate: 43200 }, // keep a copy for 12 hours
});
if (res.status === 200) papers = (await res.json()).papers; // 204 = nothing to show
} catch (e) { /* BizGenius slow or unreachable: build the page without citations */ }
// Then render papers with your own template, e.g. in JSX:
// {papers.map(p => <li key={p.id}><a href={p.url}>{p.title}</a> <i>{p.journal}</i>, {p.year}</li>)}import requests
papers = []
try:
r = requests.get("https://www.bizgeniusapp.com/api/render.php", params={
"key": "YOUR_WIDGET_KEY",
"domain": "www.example.com", # your website's domain
"part": "list",
"format": "json",
"a[maxnum]": "10",
"a[order]": "publicationDate",
}, timeout=3)
if r.status_code == 200: # 204 = nothing to show
papers = r.json()["papers"]
except requests.RequestException:
pass # build the page without citations
for p in papers: # escape when you print into HTML
print(p["title"], p.get("journal"), p.get("year"))<?php
$json = @file_get_contents('https://www.bizgeniusapp.com/api/render.php?' . http_build_query([
'key' => 'YOUR_WIDGET_KEY',
'domain' => 'www.example.com', // your website's domain
'part' => 'list',
'format' => 'json',
'a' => ['maxnum' => '10', 'order' => 'publicationDate'],
]), false, stream_context_create(['http' => ['timeout' => 3]]));
$papers = json_decode($json ?: '{}', true)['papers'] ?? []; // empty on 204 or failure
foreach ($papers as $p) { ?>
<li><a href="<?= htmlspecialchars($p['url'] ?? '') ?>"><?= htmlspecialchars($p['title']) ?></a>
<i><?= htmlspecialchars($p['journal'] ?? '') ?></i>, <?= (int)($p['year'] ?? 0) ?></li>
<?php }curl -G "https://www.bizgeniusapp.com/api/render.php" \
--data-urlencode "key=YOUR_WIDGET_KEY" \
--data-urlencode "domain=www.example.com" \
--data-urlencode "part=list" \
--data-urlencode "format=json" \
--data-urlencode "a[maxnum]=10"A citation list (part=list) looks like this, shortened:
{
"total": 2344,
"start": 0,
"papers": [
{
"id": 4436751,
"title": "Regional brain volume abnormalities in Lesch-Nyhan disease and its variants: a cross-sectional study",
"authors": ["David J Schretlen", "Mark Varvaris", "Tiffany E Ho", "..."],
"journal": "The Lancet. Neurology",
"year": 2013,
"publication_date": "2013-12-01",
"volume": "12", "issue": "12", "pages": "1151-8",
"doi": "10.1016/s1474-4422(13)70238-2",
"pubmed_id": "24383089",
"url": "https://europepmc.org/articles/pmc3932536?pdf=render",
"impact_factor": 54.6,
"times_cited": 44,
"snippet": "... created with the xjView (version 8.11 ...",
"ai_summary": "In this study, the xjView product (version 8.11) was used to ...",
"tags": ["VBM", "Lesch-Nyhan disease"]
}
]
}Each paper can have id, title, authors, journal, year,
publication_date, volume, issue, pages, doi, pubmed_id,
url, impact_factor, times_cited, products, snippet,
abstract, ai_summary and tags. Fields with no value are left out. snippet,
ai_summary, tags and times_cited are included unless you turn them off
(a[show-snippet]=no ...); abstract only with a[show-abstract]=yes.
| part | What it returns |
|---|---|
listlist-top | Your citations, or your top citations by impact factor:
{"total", "start", "papers": [...]}. Up to 100 per call; page with start.
total is null for list-top. |
count-total-year-monthcount-total, count-year, count-month |
Your citation counts: {"total": 8296, "this_year": 814, "last_month": 172} |
count-product | One product's count, with a[product-name]:
{"product": "180", "dateRange": "all", "count": 2090}. With products[], up to 100 at once:
{"results": [...]}. |
aisummary | A short paragraph about your citations (see AI Summary):
{"subject", "total", "this_year", "year", "countries", "institutions": [...], "companies": [...], "journals": [...], "text"} |
top-journaltop-universitytop-companytop-university-and-company |
The names and logos in your logo strips: {"items": [{"name": "Blood", "logo": "https://..."}]} |
list-figure | Figures from citing papers, licensed for commercial display: {"total", "start",
"figures": [...]}, 10 per call. Each has label, caption, image_url,
pdf_url and paper. |
list-new-papers-pubmedlist-new-papers-pmc | The latest PubMed or PubMed Central
papers for a[keyword] (search results, not your citations): {"total", "start", "papers": [...]},
up to 20 per call. |
Good practice (both formats)
- Keep a copy for 12 to 24 hours. Your citations change daily at most, so there's no need to call us on every page view.
- Wait at most 3 seconds. If we're slow or unreachable, build the page without citations (leave the box empty) and try again later.
- A 204 response means "nothing to show", for example a product with no citations yet. Leave the box empty.
- Send the same settings the box has. The same
a[...]as itsdata-*attributes, and the samesourcekeywordas yourcitation.min.jsURL, so the server copy and the widget show the same papers. - Print ready-made HTML as it is; escape data. The HTML is ready to use. In the JSON, all text is plain text: escape it when you print it into HTML.
Tip: give AI a paragraph to quote
The AI Summary box is the part AI assistants quote most easily: one paragraph with your citation counts, how many countries the research comes from, and well-known institutions, companies and journals among the citing papers. Every name in it appears in at least one approved citing paper. Add it to a product page or your publications page; the widget fills it in the browser, and all three setups above fill it on the server.
<!-- on a product page -->
<div class="bizgenius-citation-widget-container-aisummary" data-product-name="180" data-label="Pertussis Toxin"></div>
<!-- on your publications page: leave out data-product-name -->
<div class="bizgenius-citation-widget-container-aisummary" data-label="Your Company"></div>"Researchers have cited Pertussis Toxin in 2,090 peer-reviewed publications, 93 of them from 2026. Citing authors work in at least 57 countries and regions, at institutions such as Harvard University, Johns Hopkins University, and Shanghai Jiao Tong University ..."
Check that it works
- Open a page with the widget and choose View Page Source (not Inspect: Inspect shows the page after JavaScript has run).
- Search the source for the title of a paper you see in the widget. If it's there, AI crawlers can read your citations. With
setups 1 and 2, and with the feed's ready-made HTML, you'll find it inside
<div class="bizgenius-ssr" ...>.
Or from a terminal, fetching the page the way ChatGPT's crawler does (a number above 0 means it works):
curl -s -A "GPTBot" https://www.example.com/publications/ | grep -c "bizgenius-ssr"Nothing there? Check that the boxes are in the HTML and empty (see Before you start), clear your page cache, and reload. Still stuck? Contact us with the page address and we'll take a look.