Skip to content

Documentation

AdFlow documentation

Everything the free AdFlow plugin and AdFlow Pro can do, and how to set it up. Sections marked Pro need the AdFlow Pro add-on; everything else is in the free plugin.

30 free and 23 Pro guides on one page. Requirements: WordPress 6.4+ · PHP 7.4+ · GPLv2

Getting started

Requirements Free #

  • WordPress 6.4 or newer.
  • PHP 7.4 or newer.
  • A Google AdSense account for AdSense ads. Your own banners, text ads and other networks' code work without one.
  • AdFlow Pro is an add-on: it needs the free AdFlow plugin installed and active.

AdFlow adds one menu, AdFlow, with these screens: Dashboard, Ad Units, Placements, Reports, Earnings (Pro), Settings and Docs. Settings are grouped in tabs rather than separate menu items.

Install and activate AdFlow Free #

  1. In WordPress, go to Plugins → Add New and search for AdFlow (plugin slug simple-google-adsense), or download it from WordPress.org.
  2. Click Install Now, then Activate.
  3. Open AdFlow → Dashboard. The setup checklist shows what is left to do.

The complete documentation is also inside WordPress under AdFlow → Docs, searchable, with links straight to each screen.

Quick start: ads live in five minutes Free #

  1. Go to AdFlow → Settings → General and paste your AdSense Publisher ID (pub-1234567890123456; the ca-pub- form also works). Click Save changes.
  2. Leave Enable Auto Ads on if you want Google to place ads for you, or turn it off and place ads yourself.
  3. Go to AdFlow → Settings → ads.txt, turn on Let AdFlow manage ads.txt and save, so AdSense stops warning "Earnings at risk".
  4. To place your own ad units: go to AdFlow → Ad Units → Add Ad Unit, enter the ad slot ID from AdSense and publish. Then switch on a placement under AdFlow → Placements, or add the AdFlow Ad block to a post.
  5. Check AdFlow → Dashboard. The Site health panel lists anything that could stop ads from showing.

New AdSense accounts and new sites can take a few days before Google fills ad spaces. Empty spaces during that time are normal.

Find your Publisher ID, ad slot IDs and layout keys Free #

  • Publisher ID: in AdSense, open Account → Settings → Account information. It looks like pub-1234567890123456. AdFlow checks the format; an ID that is not "pub-" followed by 10 to 20 digits is flagged and no ad code is sent.
  • Ad slot ID: in AdSense, open Ads → By ad unit, create a unit (Display, In-feed, In-article or Multiplex) and copy the number after data-ad-slot in the code Google shows you.
  • Layout key: in-feed ads also have a data-ad-layout-key value. Copy it into the ad unit too; in-feed ads do not serve without it.

Google's help: find your publisher ID and create an ad unit.

Dashboard, setup checklist and site health Free #

AdFlow → Dashboard shows your setup at a glance:

  • Setup checklist: add your Publisher ID, authorise your site with ads.txt, start showing ads (Auto Ads or placements). With Pro active, four more steps appear: activate your licence, connect AdSense earnings, turn on click protection and add a Pro placement.
  • Status cards: Auto Ads on or off, number of ad units, ads.txt status.
  • Site health: the most common reasons ads do not show, checked for you: Publisher ID missing or invalid, no ads configured, ads.txt problems, ads that failed to render, campaigns ending within 7 days, ads hidden for administrators, caching plugins that need purging, consent setup, statistics being blocked by the server, ads waiting for review, and another ad plugin still active after an import.
  • Shortcuts: create an ad unit, place ads automatically, inspect ads on your site, manage ads.txt.

With Pro, the Dashboard also shows today's AdSense earnings, your licence status and whether the earnings dashboard is connected.

AdSense setup

Auto Ads Free #

Auto Ads let Google decide where ads go on each page. Go to AdFlow → Settings → General:

  • Auto Ads → Enable Auto Ads: on by default. The Auto Ads code is added only once a valid Publisher ID is saved.
  • Turn off on: tick content types that should never show Auto Ads (for example products), and list single posts or pages under Specific posts or pages (IDs, comma separated), such as contact or checkout pages.

On excluded pages, AdFlow still loads the AdSense library for your own ad units, but without the Auto Ads part, so only the units you placed appear.

Which Auto Ads formats Google uses (anchor, vignette, in-page) is set in your AdSense account under Ads → By site, not in WordPress.

Auto Ads and your own placements work together: Google fills the rest of the page around the ads you placed.

With Pro, the AdFlow Ads box in the post editor can turn off Auto Ads on a single post. See Per-post ad controls.

ads.txt manager Free #

ads.txt tells ad buyers you are allowed to sell ads on your site. Without it, AdSense shows "Earnings at risk" and may limit ads.

  1. Add your Publisher ID under AdFlow → Settings → General first.
  2. Go to AdFlow → Settings → ads.txt and turn on Serve ads.txt → Let AdFlow manage ads.txt (off by default).
  3. The AdSense line is added automatically from your Publisher ID: google.com, pub-…, DIRECT, f08c47fec0942fa0.
  4. Under Other ad networks, paste one line per network exactly as the network gives it to you (domain, account ID, DIRECT or RESELLER, optional certification ID). Comments and variables such as contact= are allowed.
  5. Check the Preview, then click Save changes.

The Status panel fetches your live /ads.txt the way Google does and shows Authorized or Needs attention with the reason. Click Check again after a change. Good results are cached for 12 hours, problems are re-checked after 10 minutes (an unreachable site after an hour).

Lines that do not follow the ads.txt format are listed in red and left out of the file. A physical ads.txt file in your site root is served by the web server instead of AdFlow's; delete or rename it, or copy its lines into AdFlow. If WordPress is installed in a subdirectory (for example example.com/blog), WordPress cannot answer for /ads.txt at the domain root: upload the file shown in the preview to the root yourself.

General settings: manual ads, ad label, admin visibility and data Free #

Go to AdFlow → Settings → General.

SettingDefaultWhat it does
Publisher IDemptyYour AdSense Publisher ID. Needed for every AdSense ad and for ads.txt.
Enable Auto AdsonAdds Google's Auto Ads code to every page (see Auto Ads).
Show manual adsonTurn off to hide every manual ad (placements, block, widget, shortcodes) at once without deleting anything.
Ad labelNo labelText above each manual AdSense ad: Advertisements or Sponsored Links, the only two labels AdSense allows.
Do not show ads while I am logged inoffHides all ads from logged-in administrators, so you never click your own ads. Check your pages in a private window while it is on.
Also delete all AdFlow ads, settings and statisticsoffUnder Your data. Only when this is on does deleting the plugin remove everything. See Your data and uninstalling.

Your own banners and text ads carry their own disclosure label (see Your own ads); the Ad label setting applies to AdSense units.

Ad units and placing ads

Ad units: create once, use everywhere Free #

An ad unit is an ad you set up once and reuse in placements, the block, the widget and shortcodes. Change it once and every place updates. Go to AdFlow → Ad Units → Add Ad Unit, give it a name (for example "Sidebar 300x250"), choose the Ad type and publish.

GroupAd type
Google AdSenseDisplay ad, In-article ad, In-feed ad, Multiplex ad
Your own adsImage banner, Text ad, Custom code (other ad networks, HTML/JS)
RotationRotation group (several ads take turns)
Other ad serversGoogle Ad Manager (GPT), with AdFlow Pro

The Ad Units list shows each unit's type, slot or link, status (Running, Scheduled, Expired), the last 30 days of impressions and clicks for your own ads, which placements use it, and its shortcode (click to copy).

The Use this ad unit box on the edit screen shows the Will this ad show? check (see Will this ad show?), 30-day statistics for your own ads, a link to choose a placement and the shortcode.

A trashed ad unit is restored with its previous status, so a live ad comes back live.

AdSense unit settings Free #

For AdSense types, match the type of the unit you created in AdSense. AdFlow outputs Google's exact markup for each type.

FieldUsed byNotes
Ad slot IDAll AdSense typesThe data-ad-slot number from AdSense (Ads → By ad unit). Required.
SizeDisplay adResponsive (recommended), Rectangle, Horizontal or Vertical.
Full width on mobileDisplay ad, Responsive sizeOn by default. Lets the ad use the full screen width on phones.
Layout keyIn-feed adThe data-ad-layout-key value. In-feed ads do not serve without it.

In-article ads are output as fluid, centred in-article units; Multiplex ads use the autorelaxed format.

With Pro, every unit also gets Reserved height (desktop and mobile) and Sticky in sidebars. See Lazy loading, reserved height and sticky sidebar ads.

Your own ads: image banners, text ads and custom code Free #

Run sponsor, affiliate or house ads next to AdSense, or code from any other network.

  • Image banner: choose an image from the Media Library and add Alternative text. Tip: avoid words like "ad" or "banner" in the file name, as ad blockers hide those.
  • Text ad: Headline, Text and an optional Button text such as "Learn more".
  • Custom code: paste code from Google Ad Manager, Media.net or any network. It is output exactly as entered. Only users allowed to post unfiltered HTML (administrators on a single site) can add or edit it.

For banners and text ads, the Link is where a click goes. Links always get rel="sponsored" as Google asks; Open in a new tab is on by default and Also add rel="nofollow" is optional. Links can carry tracking macros: {ad_id}, {placement}, {site} and {cachebuster}, for example https://example.com/?utm_source=mysite&utm_content={placement}.

Delivery box

SettingDefaultWhat it does
Disclosure labelSponsoredSponsored, Advertisement or No label, shown above the ad. Laws in many countries require paid placements to be marked.
SchedulenoneOptional start and end date and time, in your site's time zone. Outside it the ad hides itself, even on cached pages.
When not runningShow nothingChoose an AdSense unit to show in its place before the start or after the end, so the space keeps earning.
StatisticsonCount impressions and clicks for this ad (see Statistics).

Your own ads use neutral class names, so generic ad-blocker rules are less likely to hide them. Custom code that could still be held back by a browser-side rule (consent, schedule, Pro caps, days and hours or countries) waits until the rule allows it, so it never loads and is then hidden.

With Pro, the Delivery box adds a frequency cap, dayparting, country targeting, advertiser details, performance emails and a campaign price. See Frequency caps and dayparting, Country targeting and Advertiser reports, emails and pricing.

Rotation groups Free #

A rotation group shows one of its ads per page view. Create one under AdFlow → Ad Units → Add Ad Unit with the type Rotation group.

  1. Under Ads in this group, pick the ads and give each a Weight (1 to 100). Need more rows? Save and more empty rows appear.
  2. Choose the Rotation: Random, by weight (weight 3 is shown three times as often as weight 1) or In order - each visitor sees the next ad.
  3. Publish, then use the group like any ad unit: in a placement, the block, the widget or a shortcode.

The choice is made in the visitor's browser, so rotation keeps working with page caching, and the ads that were not chosen load nothing and count nothing. Only ads that may show right now take part: ads outside their schedule, and with Pro capped, out-of-hours or other-country ads, are skipped.

Mix sponsor ads with an AdSense unit in the same group so the space is never empty.

Automatic placements Free #

Placements insert an ad unit into your content without editing posts. Go to AdFlow → Placements. If no ad unit exists yet, create one first.

PlacementWhereOptions
Before contentAbove the first paragraph of the post.Show on (post types)
After paragraphInside the post, after the chosen paragraph. Skipped when the post is shorter.After paragraph (1 to 50, default 3), Show on
After contentBelow the last paragraph of the post.Show on
  1. Switch a placement on with its toggle.
  2. Choose an ad unit (or a rotation group) from Choose an ad unit….
  3. Under Show on, tick the post types (Posts is ticked by default). No post type ticked means the placement shows nowhere.
  4. Click Save placements.

Paragraphs inside quotes, tables, lists, figures, code blocks, details, asides, navigation and forms are not counted, so an ad never lands inside them. Placements only run on the main content of single posts and pages, not in excerpts, feeds or related-post widgets.

Pro adds Middle of the article (after X%), Before comments, Between posts, Sticky anchor, Popup and three WooCommerce placements, plus targeting, scheduling and A/B tests on every placement. See More placements.

AdFlow Ad block Free #

In the block editor, add the AdFlow Ad block (Widgets category) and choose a saved unit under Ad settings → Ad unit. The block supports wide and full alignment.

You can also choose - Enter the ad manually - and fill in an AdSense Ad Slot ID, Ad Type, Ad Format, Full Width Responsive and, for in-feed ads, a Layout key. For Google's exact in-article or multiplex code, save the ad under AdFlow → Ad Units and pick it in the block instead.

Block attributeTypeDefaultMeaning
adIdnumber0ID of a saved ad unit. When set, the other attributes are ignored.
adSlotstringemptyAdSense slot ID for a manually entered ad.
adTypestringbannerbanner, inarticle, infeed or matched_content.
adFormatstringautoauto, fluid, autorelaxed, rectangle, horizontal or vertical.
fullWidthResponsivebooleantrueFull-width responsive on mobile.
layoutKeystringemptyIn-feed layout key. With adType infeed, output becomes an in-feed unit.

The block name is simple-google-adsense/adsense-ad.

AdFlow Ad widget Free #

  1. Go to Appearance → Widgets (classic widget areas) and add the AdFlow Ad widget to a sidebar.
  2. Enter an optional Title and choose the Ad unit.
  3. Save.

In block-based widget areas you can use the AdFlow Ad block instead.

With Pro, turn on Keep in view while scrolling on the unit and place the widget last in the sidebar to make it sticky on desktop.

Shortcodes Free #

The recommended shortcode shows a saved ad unit (the ID is shown in the Ad Units list):

[adflow id="123"]
AttributeRequiredMeaning
idyesAd unit ID.
stylenoInline CSS for the wrapper, for example margin:20px 0.
classnoExtra CSS classes for the wrapper.

AdSense shortcodes without a saved unit

[adsense ad_slot="1234567890"]
[adsense_banner ad_slot="1234567890"]
[adsense_inarticle ad_slot="1234567890"]
[adsense_infeed ad_slot="1234567890" layout_key="-fb+5w+4e-db+86"]
[adsense_multiplex ad_slot="1234567890"]
AttributeDefaultMeaning
ad_slotemptyAdSense slot ID. Required.
idemptyOn [adsense]: show a saved ad unit instead, like [adflow].
ad_formatautoFormat of display units ([adsense], [adsense_banner]).
full_width_responsivetrueFull-width responsive on mobile for auto format.
layout_keyemptyIn-feed layout key. On [adsense], giving it outputs an in-feed unit.
ad_clientsite Publisher IDAnother ca-pub-… ID for this ad. Only honoured when the post was written by a user allowed to post unfiltered HTML.
style, classemptyWrapper CSS and classes.

[adsense_matched_content] is an alias of [adsense_multiplex]. The plain [adsense] shortcode always outputs a display unit unless layout_key is given; use the typed shortcodes or a saved unit for in-article and multiplex markup.

Pro adds [adflow_advertise] for self-serve ad sales. See Sell ads directly.

Will this ad show? and the Ad Inspector Free #

Will this ad show?

Every published ad unit shows a Yes or No answer in its Use this ad unit box, with each check listed: manual ads switched on, Publisher ID and slot ID set, layout key for in-feed ads, image, headline or code present for your own ads, schedule status, ads.txt authorisation, which placements use the unit, and whether ads are hidden while you are logged in. Pro adds its own checks (for example caps, dayparting, countries and Ad Manager settings).

Ad Inspector

  1. Open any page of your site while logged in (the WordPress toolbar must be visible).
  2. In the toolbar, choose AdFlow → Inspect ads on this page. You can also use Inspect ads on your site in the Dashboard shortcuts.
  3. Every ad is outlined with its unit, slot and placement and marked Filled, Unfilled (Google had no ad for this slot right now), Requested (waiting for Google) or Not requested (AdSense script blocked, lazy loading or a targeting rule). Auto ads and your own ads are labelled too.

The Inspector is available to users with access to AdFlow. If Do not show ads while I am logged in is on, it tells you so; check in a private window instead.

Privacy, statistics and reports

Statistics for your own ads Free #

AdFlow counts impressions, viewable impressions and clicks of your image banners, text ads and custom code. AdSense statistics come from Google; AdFlow does not count AdSense impressions itself.

  • Impression: the ad was displayed on a page a real browser showed (for banners, the image loaded). Hidden, prefetched and prerendered pages do not count.
  • Viewable impression: at least 50% of the ad was on screen for at least one continuous second (IAB/MRC standard).
  • Click: a link in the ad was opened. A scroll that starts on the ad is not a click.
  • Not counted: known bots and crawlers, headless browsers, link previews, prefetches and logged-in administrators.

Counting is cookieless: only daily totals are stored, with no IP addresses, cookies or visitor IDs, and it works with page caching. Days follow your site's time zone.

Settings

Go to AdFlow → Settings → Tracking.

SettingDefaultWhat it does
Count impressions and clicksonStatistics for all your own ads. Each ad can also be excluded in its Delivery box.
Do not count logged-in administratorsonChecked on the server, so it also works on cached pages.
Wait for statistics consentoffOnly count visitors who accepted statistics cookies in a consent plugin that supports the WP Consent API.
Counting clicksIn the pageIn the page: links go straight to the advertiser. Through a redirect link on your site (/?afx-go=…): counts on the server and works without JavaScript.
Keep data for24 months1 to 120 months. Older daily totals are removed by a daily task.

AdFlow adds a suggested paragraph about ads and statistics to the WordPress privacy policy guide (Settings → Privacy).

Reports Free #

AdFlow → Reports shows impressions and clicks of your own ads:

  • Totals for Impressions, Viewable (with viewability rate), Clicks and Click-through rate.
  • A chart of Impressions per day.
  • A By ad table with status, impressions, viewability, clicks and CTR.

Choose Last 7 days or Last 30 days, filter to a single ad, and click Apply. The Full report link on an ad unit opens the report for that ad.

Pro adds Last 90 days, Last 12 months and custom dates, CSV export, breakdowns by placement, device and advertiser, and sponsor eCPM next to your AdSense RPM. See Full reports and CSV export. AdSense earnings are under AdFlow → Earnings.

Teams and switching plugins

Roles and permissions Free #

Give editors, an ad manager or your sales team access without making them administrators. Go to AdFlow → Settings → Access, tick the permissions per role and click Save access.

PermissionAllows
Create & edit adsCreate and edit ad units that are not live, and change placements.
Publish ads (no review)Publish ads directly, and edit, trash or delete live ads.
View reportsOpen Reports (and, with Pro, Earnings and the REST statistics endpoint).

Any role with at least one permission also gets the AdFlow menu. Administrators (anyone who can manage options) always have full access, and the Settings screen stays with administrators.

Approval workflow Free #

A user who may create but not publish ads gets Submit for Review instead of Publish. When an ad is submitted:

  1. Everyone with Publish ads (no review) is emailed (up to 20 people) with a link to the ad.
  2. The Dashboard shows Ads waiting for review with a Review link.
  3. A reviewer checks the ad and publishes it.

Live ads are read-only for users without the publish permission, so saving can never pull a running ad off the site while it waits for review.

Switch from Advanced Ads, Ad Inserter, AdRotate or WP QUADS Free #

  1. Go to AdFlow → Settings → Switch to AdFlow. AdFlow lists what it found from each plugin. The other plugin does not have to be active.
  2. Click Import (or Import new items for a plugin already imported). Ads, rotation groups, placements and, for AdRotate and WP QUADS, statistics are copied. Nothing in the other plugin is changed, and importing again never creates duplicates.
  3. Review the report. AdSense code for your Publisher ID becomes a native AdSense unit, linked banners from your Media Library become image ads with tracking, and everything else is kept as custom code so it looks exactly as before. Placements only fill AdFlow placements that are still empty. Notes list anything worth a look, such as paused ads imported as drafts or positions AdFlow placed differently.
  4. Deactivate the old plugin so ads are not shown twice. Its shortcodes keep working through AdFlow.
PluginShortcodes AdFlow answers
Advanced Ads[the_ad id=""], [the_ad_group id=""]
Ad Inserter[adinserter block=""]
AdRotate[adrotate banner=""], [adrotate group=""]
WP QUADS[quads id=""]

Custom code can only be imported by a user allowed to post unfiltered HTML. Code containing PHP is imported for review but never run. A Publisher ID found in the other plugin is copied into Settings.

Pro: earnings, protection and performance

Install AdFlow Pro and activate your licence Pro #

  1. Keep the free AdFlow plugin installed and active. Pro is an add-on and shows an error notice with a Fix it link when the free plugin is missing or out of date.
  2. Download adflow-pro.zip from your account, then go to Plugins → Add New → Upload Plugin, upload it and activate it. You are taken to the AdFlow Dashboard, which now shows the Pro setup steps.
  3. Go to AdFlow → Settings → License, paste your License key (in your purchase email and your account) and click Activate license.

The licence enables one-click updates and priority support. Pro features keep working if a licence expires; renew to receive updates again. Use Deactivate on this site to move a licence to another site. When all activations are used, the licence screen tells you so.

When Pro is active, the Upgrade to Pro menu item disappears and Pro options appear inside the existing screens: new Settings tabs (Click protection, Performance, Sell ads, Import / Export, License, Activity log), new placements and fields on ad units.

Connect the AdSense earnings dashboard Pro #

The earnings dashboard reads your AdSense reports through Google's AdSense Management API with read-only access. Only administrators can connect it. You connect it once with your own Google Cloud OAuth client (about five minutes):

  1. In Google Cloud Console, create or select a project and enable the AdSense Management API.
  2. Configure the OAuth consent screen (External) and set its publishing status to In production. Apps left in "Testing" lose access every 7 days and must be reconnected.
  3. Create an OAuth client ID of type Web application. In WordPress, go to AdFlow → Earnings, click Copy next to Authorized redirect URI and add that address as an authorised redirect URI of the client.
  4. Paste the Client ID and Client Secret into AdFlow → Earnings and click Save credentials.
  5. Click Connect with Google, sign in with the Google account that owns your AdSense account and allow access to AdSense.

AdFlow picks the AdSense account that matches the Publisher ID in Settings → General. If none matches, it uses the first account and says so.

To keep the credentials out of the database, define them in wp-config.php; the form is then replaced by a note:

define( 'ADFLOW_GOOGLE_CLIENT_ID', '1234567890-abc.apps.googleusercontent.com' );
define( 'ADFLOW_GOOGLE_CLIENT_SECRET', 'your-client-secret' );

If a one-click Connect with Google button is offered at the top of the Earnings screen without any credentials, that connection service is available for your site and you can use it instead; the own-project setup then sits under Advanced: use your own Google Cloud project instead. When no such button is shown, use the steps above.

Changing the Client ID or secret revokes the old connection. Disconnect removes the connection from WordPress.

Earnings dashboard and widget Pro #

Once connected, AdFlow → Earnings shows (for users with the View reports permission):

  • Key figures: Estimated earnings, Page views, Page RPM, Impressions, Clicks and Page CTR.
  • Daily earnings chart for the chosen period.
  • Ad unit performance (A/B testing): earnings, impressions, clicks, CTR and RPM per AdSense ad unit. Units that match an AdFlow ad unit (by slot ID) are linked and tagged AdFlow. The unit with the highest RPM is marked Winner once at least two units each have enough impressions (1,000 or 10% of the busiest unit, whichever is higher).
  • Top pages: the ten pages that earn the most, with page views, page RPM and clicks.

Choose Today, Yesterday, Last 7 days (default), Last 30 days or This month, and tick This site only to leave out other sites on the same AdSense account. Reports are cached for one hour; Refresh fetches fresh data. Today's earnings are estimates and may change.

Administrators also get an AdSense Earnings (AdFlow Pro) widget on the WordPress Dashboard, and an Earnings today card with this month's total on the AdFlow Dashboard.

Invalid-click protection Pro #

Repeated clicks from one visitor are the most common reason AdSense limits accounts. Go to AdFlow → Settings → Click protection:

SettingDefaultRange
Protect my ads from repeated clicksoff-
Limit: Block after N clicks within N hours3 clicks in 24 hours1 to 20 clicks, 1 to 720 hours
Block for7 days1 to 365 days

A visitor who reaches the limit is flagged in their browser and stops receiving ads for the block period. Flagged visitors never even load the AdSense script, so no ad is ever hidden (hiding ads breaks AdSense policy). The tab shows how many visitors have been blocked so far.

Click protection reduces risk; no tool can guarantee Google's decisions about your account.

Ad density and ad-blocker message Pro #

Also on AdFlow → Settings → Click protection:

  • Max manual ads per page: keeps pages within Google's "more content than ads" guidance. 0 (default) means no limit; up to 50. A rotation group counts as one ad. Auto Ads are managed by Google and not counted.
  • Ad-blocker message: turn on Show a message to ad-block users to show a polite, dismissible note to visitors who block ads, and edit its Text. Off by default.

Lazy loading, reserved height and sticky sidebar ads Pro #

Lazy loading

Go to AdFlow → Settings → Performance and turn on Lazy load manual ads (off by default). Ads are requested only when they are about to scroll into view: a faster first load and better viewability. Start loading sets how far below the screen an ad starts loading (default 300 px, 0 to 2,000). Auto Ads are managed by Google and not affected.

Reserved height

To stop pages jumping when ads load, edit an ad unit and set Reserved height for Desktop and Mobile in pixels. Typical values are 90, 250 or 280; 0 turns it off.

Sticky sidebar ads

On an ad unit, turn on Sticky in sidebars → Keep in view while scrolling and place the unit last in your sidebar (widget or block). On desktop it stays in view while the visitor scrolls past a long article. Google allows this for vertical ads in a sidebar; in-content ads are never made sticky.

Per-post ad controls Pro #

Administrators see an AdFlow Ads box in the sidebar of the editor for every public post type:

  • No ads on this page: turns off every AdFlow ad and Auto Ads on that post or page.
  • No Auto Ads on this page: keeps your own placements and units but turns off Google's Auto Ads there.

Pro: placements, targeting and selling

More placements Pro #

With Pro, AdFlow → Placements gains these placements, grouped under Inside posts & pages, Post lists & archives, Site-wide and WooCommerce:

PlacementWhereOptions (default)
Middle of the article (after X%)After the paragraph at X% of the article, so it scales with post length.Position in the article: 10 to 90% (50%)
Before commentsAbove the comments section (classic themes).Show on
Between postsIn the post list on home, category, tag and search pages, in classic and block themes.After every 1 to 20 posts (3)
Sticky anchor (bottom of screen)One small closable bar per page.-
Popup (your own ads)A centred, closable box.See Popup and sticky anchor
WooCommerce: above product gridShop and product category pages.-
WooCommerce: below product summarySingle product pages, above the tabs.-
WooCommerce: after add-to-cartSingle product pages, below the add-to-cart button.-

The WooCommerce placements appear when WooCommerce is active. Every placement, free or Pro, also gets targeting, scheduling and A/B testing (see Targeting, scheduling and A/B tests).

Targeting, scheduling and A/B tests Pro #

On AdFlow → Placements, open Targeting, schedule & A/B test under any placement:

RuleOptionsWhere it is checked
DeviceAll devices, Mobile only, Tablet & desktop onlyIn the browser
VisitorsEveryone, Logged-out visitors, Logged-in usersOn the server
Traffic sourceAny, Search engines only, Not from search enginesIn the browser
Posts older than (days)0 = any ageOn the server
Start date / End dateInclusive, site time zoneOn the server
Only in these categoriesNone selected = all (not applied to WooCommerce placements)On the server

Browser rules work with page caching, and an ad that should not show never requests an ad from Google. Date rules make it possible to schedule AdSense units, not only your own ads.

A/B test

Under A/B test: also rotate with, tick extra ad units. Each page view shows one of the placement's unit and the ticked units. Compare them under AdFlow → Earnings → Ad unit performance.

The A/B choice is made when the page is generated. With full-page caching, each cached copy keeps its unit until the cache is refreshed; for a choice on every view of a cached page, use a rotation group.

Google Ad Manager (GPT) units Pro #

Create an ad unit with the type Google Ad Manager (GPT) (under Other ad servers):

FieldExampleNotes
Ad unit path/1234567/sidebarFrom Ad Manager → Inventory → Ad units → Tags: network code followed by the ad unit code.
Sizes300x250, 336x280, fluidComma separated. "fluid" for native ads.
Mobile sizes320x100, 300x250Optional. Used on screens narrower than 768px; empty = the sizes above.
Key-valuessection={category}One key=value per line (up to 20), for line item targeting. Separate several values with commas.
Empty slots-Collapse the space when no ad is returned.

Key-value macros: {post_id}, {post_type} (home or archive on list pages), {category} (first category slug) and {placement} (manual for shortcode, block or widget).

section={category}
pos=sidebar

GPT loads once, asynchronously, only on pages that show such a unit, and respects placement rules, click protection and "Load ads after consent". Whether a slot fills depends on your line items in Ad Manager.

Frequency caps and dayparting Pro #

In the Delivery box of your own ads (image, text, custom code):

  • Frequency cap: stop showing this ad to a visitor after N impressions per day (0 = no cap, up to 100). The count is stored only in the visitor's browser.
  • Dayparting: tick days (Mon to Sun) and choose hours From and to, in your site's time zone. Nothing ticked = every day.

Both rules are applied in the browser, so they work with page caching. In a rotation group, an ad that is capped or out of hours is skipped and another ad is shown.

Country targeting Pro #

Sell a sponsorship to one market only. In the Delivery box of your own ads, set Countries to Only in or Everywhere except and list two-letter country codes, for example US, CA, GB. Put the ad in a rotation group with an AdSense unit so other visitors still see an ad.

Where the country comes from

  1. A country header from your CDN or host, if present: Cloudflare (turn on "IP Geolocation"), Amazon CloudFront, Google Cloud, Vercel, or a host header such as X-Country-Code, GeoIP-Country-Code or X-Geo-Country, or a server GeoIP module.
  2. Otherwise the optional free country database: click Use the free country database in the Countries field. It downloads DB-IP's free database (about 5 MB, CC BY 4.0) to your uploads folder and refreshes it monthly. Visitor IPs are looked up on your own server and never stored or sent anywhere. Remove turns it off.

The field shows which source was detected and your own country. The country is looked up per visitor in the browser, so targeting works on cached pages. No IP addresses are stored.

Without any country source, "Only in" ads stay hidden and "Everywhere except" ads show to everyone. The Dashboard warns you when ads use countries but no source is found.

Advertiser reports, emails and pricing Pro #

In the Delivery box of your own ads:

  • Advertiser: company name and optional contact email, used for reports, the report link and reminders. Turn on Email the advertiser before the campaign ends to include them in expiry reminders.
  • Performance emails: Off, Weekly (Mondays, last 7 days) or Monthly (1st, last month). The advertiser receives impressions, viewability, clicks, CTR, campaign totals and the live report link. Email me a preview sends you the same email.
  • Campaign price and currency (default USD): what the advertiser pays for the whole campaign. Used to work out the sponsor eCPM, shown next to your AdSense RPM in Reports.

Report link

In the ad's sidebar, Advertiser report → Create report link makes a read-only page with the ad's impressions, viewable impressions, clicks and CTR, day by day, with no login. Turn off link disables it at any time.

Expiry reminders

For ads with an end date, AdFlow emails the site's administration email address (Settings → General in WordPress) once in the 7 days before the campaign ends and once after it has ended, with the numbers so far and an edit link; the advertiser is included when the reminder option is on. The Dashboard also lists campaigns ending within 7 days.

Emails are sent with WordPress mail. If previews do not arrive, check that your site can send email (an SMTP plugin usually helps).

Sell ads directly (self-serve) Pro #

  1. Create a rotation group (AdFlow → Ad Units → Add Ad Unit, type Rotation group) and put it in a placement. Sold ads then take turns in that spot automatically.
  2. Go to AdFlow → Settings → Sell ads and fill in a package: Name, Description, Ad (Image banner, Text ad, or Banner or text; banner size such as 300x250; number of days, default 30), Shows in (the rotation group approved ads join, or I will place approved ads myself) and Price & payment (price, currency and your payment link: a Stripe Payment Link, PayPal.me or any checkout page). Click Save packages. Up to 20 packages; For sale hides a package without deleting it, and emptying the name removes it.
  3. Create an "Advertise with us" page containing [adflow_advertise].
  4. Advertisers choose a package, enter their name, email and link, upload a banner (JPG, PNG, GIF or WebP, up to 2 MB) or write a text ad, and optionally pick a start date. After submitting they see Pay … now, which opens your payment link with the order number added as ?order= (and client_reference_id).
  5. The order waits under AdFlow → Ad Units as Pending, and everyone who may publish ads is emailed. Its Order box shows the package, price, days and requested start. Check the creative and the payment, click Mark as paid, then Publish.
  6. On publishing, the ad gets its run dates (from the requested day or now, for the paid number of days), joins the package's rotation group, and the advertiser is emailed with the dates and a live report link. They also receive weekly performance emails. The ad stops by itself at the end date.

No card data ever reaches your site: payment happens on your payment provider's page. The form works on cached pages and is protected against spam with a signed timestamp, a honeypot field and a limit of 5 orders per visitor and 30 per site per hour. Deleting a rejected order also deletes the banner uploaded with it.

Full reports and CSV export Pro #

With Pro, AdFlow → Reports adds:

  • Date ranges Last 90 days, Last 12 months and Custom dates (from and to).
  • Export CSV for the chosen range and ad.
  • Breakdowns By placement (including "Shortcode, block or widget"), By device (Mobile, Desktop & tablet) and By advertiser.
  • Advertiser and Sponsor eCPM columns in the By ad table.
  • Direct deals vs AdSense: your best sponsor eCPM next to your AdSense RPM for the last 30 days (with the earnings dashboard connected), so you can price deals with confidence.

Export and import between sites Pro #

Go to AdFlow → Settings → Import / Export.

  • Export: Download export file saves your settings, ad units, placements, ads.txt, privacy and statistics settings, Pro settings and ad packages as one JSON file. The AdSense connection and the licence are not included.
  • Import: choose an AdFlow export file (up to 2 MB) and click Import. It replaces your current AdFlow settings and adds the ad units from the file. Ad units you already have (same name and type) are kept, not duplicated, and placements, rotation groups, fallbacks and packages are pointed at the right units.

Importing replaces your current settings. Custom code in imported ad units is only kept when the importing user may post unfiltered HTML.

Activity log Pro #

AdFlow → Settings → Activity log records every change to ad units (created, updated, trashed, restored, deleted), placements and settings, access, the licence and incoming ad orders: when, who and what. Entries are kept for 12 months.

Developers

Filters Free #

FilterArgumentsUse
adflow_ads_allowed$allowedTurn all ads off on a request.
adflow_auto_ads_allowed$allowedWhether the Auto Ads code is printed on this request.
adflow_should_display_ad$display, $unit, $contextHide a particular ad.
adflow_ad_markup$html, $unit, $contextChange the final ad output.
adflow_ad_wrapper_attributes$attributes, $unit, $contextAdd attributes to the wrapping element.
adflow_ad_ins_attributes$attributes, $unit, $contextChange the attributes of the AdSense ins element.
adflow_ad_inline_push$push, $unit, $contextWhether the inline adsbygoogle.push() is printed.
adflow_ad_label$label, $keyChange the AdSense ad label text.
adflow_ad_types$typesRegister an ad unit type; render it with adflow_render_ad_type ($html, $unit, $context).
adflow_ad_unit$data, $postFilter a loaded ad unit.
adflow_sanitize_ad_unit$data, $input, $post_idSave extra ad unit fields.
adflow_ad_diagnostics$checks, $unitAdd lines to "Will this ad show?".
adflow_placement_types$typesRegister a placement.
adflow_placement_should_display$display, $key, $placementAdd placement conditions.
adflow_placement_ad_id$ad_id, $key, $placementChoose which unit a placement renders.
adflow_sanitize_placement$clean, $raw, $keySave extra placement fields.
adflow_paragraph_container_tags$tagsElements whose paragraphs are not counted for placements.
adflow_ads_txt_content$contentChange the served ads.txt body.
adflow_allow_ad_client_override$trusted, $ad_client, $postAllow or refuse the ad_client shortcode attribute.
adflow_review_recipients$emails, $postWho is emailed about ads waiting for review.
adflow_bot_pattern$regexUser agents that are not counted in statistics.
adflow_delete_stats_with_ad$delete, $post_idKeep statistics when an ad unit is deleted.
adflow_inspector_enabled$enabledTurn the toolbar Ad Inspector off.
adflow_health_checks$checksAdd Dashboard site health checks.
adflow_setup_steps, adflow_dashboard_cards$steps / $cardsExtend the Dashboard.
adflow_settings_tabs$tabsAdd a Settings tab (label, callback, priority).
adflow_docs_sections$sectionsAdd articles to AdFlow → Docs.
adflow_delete_data_on_uninstall$deleteForce or prevent data removal on uninstall.

$context contains source (shortcode, block, widget or placement) and placement (the placement key, or empty).

Actions Free #

ActionArgumentsFires
adflow_ad_rendered$unit, $contextAfter an ad was output.
adflow_imported$logAfter an import from another ad plugin.
adflow_purge_page_cache-When AdFlow asks page caches to purge (it already purges WP Rocket, W3 Total Cache, WP Super Cache and LiteSpeed Cache).
adflow_access_saved-After the Access tab was saved.
adflow_ad_unit_fields, adflow_ad_unit_delivery_fields$data, $postAt the end of the Ad unit settings and Delivery boxes.
adflow_ad_unit_sidebar$unitIn the Use this ad unit box.
adflow_placement_options, adflow_placement_settings_fields$key, $placement, $nameInside and below a placement's options.
adflow_daily_maintenance-Daily cron event (statistics clean-up, Pro reminders and emails).

The ad unit post type is adflow_ad; its settings are stored in the _adflow_ad post meta. Statistics are daily rows in the {prefix}afx_stats table.

Pro hooks and constants Pro #

Hook or constantUse
adflow_geo_country filter ($code)Supply the visitor's two-letter country code from your own lookup, for example a MaxMind database.
adflow_geo_client_ip filter ($ip)Correct the visitor IP used for the local country database.
adflow_pro_ab_min_impressions filter ($min)Minimum impressions before an ad unit can be the A/B winner.
adflow_pro_orders_per_hour filter ($max)How many ad orders the whole site accepts per hour (default 30).
adflow_pro_advertiser_report filter ($email, $unit, $period)Change the subject or body of advertiser performance emails.
adflow_pro_google_oauth_config filter ($config)Adjust the Google OAuth configuration of the earnings dashboard.
adflow_pro_loaded actionFires once all Pro modules are loaded.
ADFLOW_GOOGLE_CLIENT_ID, ADFLOW_GOOGLE_CLIENT_SECRETGoogle OAuth client credentials in wp-config.php.

REST API Pro #

Authenticate with an Application Password (Users → Profile → Application Passwords) or a logged-in session with a REST nonce.

EndpointPermissionReturns
GET /wp-json/adflow/v1/adsView reports or Create & edit adsAll ad units: id, title, type, status, start, end, advertiser, url, slot, tracked.
GET /wp-json/adflow/v1/ads/<id>View reports or Create & edit adsOne ad unit plus last_30_days totals.
GET /wp-json/adflow/v1/statsView reportsStatistics of your own ads.

Parameters of /stats:

ParameterDefaultMeaning
from, to-Dates as YYYY-MM-DD.
days30Last N days when no dates are given (1 to 3660).
ad0 (all)Only this ad unit ID.
bydayday, ad, placement, device or none.
curl -u "user:app-password" \
  "https://example.com/wp-json/adflow/v1/stats?from=2026-09-01&to=2026-09-30&by=placement"

The response contains from, to, timezone, totals and rows; each row has impressions, viewable, clicks, ctr and viewability (as percentages).

WP-CLI Pro #

wp adflow ads [--format=table|csv|json|yaml]
wp adflow stats [--from=YYYY-MM-DD] [--to=YYYY-MM-DD] [--days=30] [--ad=<id>] [--by=day|ad|placement|device|none] [--format=table|csv|json|yaml]

wp adflow ads lists ID, title, type, status, advertiser and end date. wp adflow stats prints rows grouped as requested and a totals line in the site's time zone. Examples:

wp adflow stats --days=7 --by=ad
wp adflow stats --from=2026-09-01 --to=2026-09-30 --by=device --format=csv > september.csv

Troubleshooting and FAQ

Ads are not showing Free #

  1. Look at the Site health panel on AdFlow → Dashboard and the ad's Will this ad show? box.
  2. Open the page while logged in and choose AdFlow → Inspect ads on this page in the toolbar. Every ad is outlined as filled, unfilled or not requested.
  3. If Do not show ads while I am logged in is on (Settings → General), you will not see ads. Check in a private window.
  4. Administrators see a notice in place of an ad that cannot render, for example "AdSense Publisher ID not configured", "Ad Slot ID is required", "Manual Ads are turned off" (turn Show manual ads back on) or "Ad unit not found" (the unit was deleted or is not published). Visitors see nothing.
  5. Purge your caching plugin after changing ads, and exclude adsbygoogle.js from JavaScript "delay" or "combine" features.
  6. With Load ads after consent on, ads wait until the consent banner is answered.
  7. In-feed units without a layout key do not serve. Placements are skipped on posts with fewer paragraphs than the chosen number, and show nowhere when no post type is ticked under Show on.
  8. Unfilled AdSense spaces are Google's decision (new site, low traffic, policy). They are not an AdFlow error.

With Pro, also check placement targeting and dates, Max manual ads per page, the AdFlow Ads box on the post, country rules, and whether you were flagged by click protection while testing (clear your cookies for the site).

ads.txt shows Needs attention Free #

MessageWhat to do
Add your Publisher ID first.Save your Publisher ID under Settings → General.
ads.txt returned HTTP 404 (or another code).Turn on Let AdFlow manage ads.txt and save. If it stays, a security or caching rule may be blocking /ads.txt; purge the cache and check your server rules.
ads.txt exists but does not contain your AdSense line.A physical ads.txt file is probably served instead of AdFlow's. Delete or rename it, or add the AdSense line to it.
Could not fetch ads.txt.Your server could not reach itself (loopback request). The live file may still be fine; open it in a browser. The check runs again after an hour.
WordPress is installed in a subdirectory.Upload the file from the Preview to your domain root manually.

After fixing, click Check again. AdSense itself can take a few days to re-crawl the file.

Statistics stay at zero Free #

  • Visits by logged-in administrators, bots and link previews are not counted by design. Test in a private window.
  • Check that Count impressions and clicks is on under Settings → Tracking and in the ad's Delivery box. Statistics are only for your own ads; AdSense numbers are in AdSense (or AdFlow → Earnings with Pro).
  • If Wait for statistics consent is on without a WP Consent API plugin, nothing is counted; the Tracking tab warns you.
  • If the Dashboard says Your server refuses statistics requests, a security plugin or firewall is blocking both the REST API and admin-ajax. Allow them for visitors. When only the REST API is blocked, AdFlow switches to admin-ajax by itself.
  • Ads without a link cannot count clicks.

Earnings dashboard connection problems Pro #

MessageFix
The redirect URI is not registered for this OAuth client.Copy the Authorized redirect URI from AdFlow → Earnings into the OAuth client in Google Cloud Console, exactly (including https and www).
Google rejected the OAuth client.Paste the Client ID and Client Secret again from the same OAuth client.
Access was denied on the Google consent screen.Allow access to AdSense. If the consent screen is in Testing, add your Google account as a test user, or set it to In production.
AdSense access was not granted.Connect again and tick the AdSense permission on the consent screen.
The AdSense Management API is not enabled for your Google Cloud project.Enable the AdSense Management API in the project that owns the OAuth client.
Google did not return a refresh token.Remove the app from your Google account's third-party access, then connect again.
Google access has expired or was revoked.Click Connect with Google again. Consent screens left in Testing expire every 7 days.
No AdSense account was found for this Google account.Connect with the Google account that owns the AdSense account.
The connection request expired or could not be verified.Reload AdFlow → Earnings and click Connect with Google again.

Ad order form messages Pro #

Message on the formCause
The form expired. Please try again.The page was older than 7 days (for example a very old cached copy) or the form was sent within 3 seconds of loading. Purge the cache of the "Advertise with us" page if it happens often.
Please fill in your name, a valid email address and a link…A required field is missing or the link is not a valid address.
Please upload a banner image or write a headline…The package needs a banner, a text ad, or one of them.
The image could not be uploaded.Not a JPG, PNG, GIF or WebP image, or larger than 2 MB.
Too many orders from your network.The per-visitor (5 per hour) or site-wide (30 per hour) limit was reached.

If the shortcode shows "No ad packages are for sale yet" (to administrators only), add a package under Settings → Sell ads and make sure For sale is on.

Your data and uninstalling Free #

Deactivating or deleting AdFlow keeps your ad units, settings and statistics, so reinstalling or switching plugins never loses them. To remove everything when the plugin is deleted, first turn on Also delete all AdFlow ads, settings and statistics under Settings → General → Your data. On multisite, each site decides for itself.

AdFlow stores no visitor IP addresses, cookies or visitor IDs for statistics. Only daily totals per ad, placement and device type are kept, for the period set under Settings → Tracking.

Frequently asked questions Free #

Is the free plugin limited?

No. There is no limit on ads, placements or sites, and no AdFlow branding on your ads.

Does AdFlow work with page caching?

Yes. Rotation, statistics, schedules and the browser-side targeting rules run in the visitor's browser, so they keep working with WP Rocket, LiteSpeed, Cloudflare and other caches. Saving an ad unit asks common caching plugins to purge.

Does AdFlow slow my site down?

The AdSense script and AdFlow's styles load only on pages that show an ad (or everywhere when Auto Ads are on, as Google requires). A broken ad never breaks the page: it is skipped and reported on the Dashboard.

Can I use AdSense and another network together?

Yes. Use Custom code units for other networks, add their lines to ads.txt, and mix them with AdSense in placements or rotation groups.

Do Pro features stop working if my licence expires?

No. Everything keeps working; the licence provides updates and support.

Can I use AdSense in the popup?

No. Google does not allow AdSense in popups, so AdFlow never shows AdSense units there. Use the sticky anchor for AdSense.

Where do I get help?

Free: the support forum on WordPress.org. Pro: priority email support with an active licence.