Meta Marketing API v26.0: The Changes of 27 October 2026 and an Audit Script for Existing Integrations

Management Summary
The Meta Marketing API lets external software manage advertising and retrieve results. Changes scheduled for 27 October 2026 affect estimates, ad content and delivery settings, including some connections using older supported versions. This matters for businesses and agencies relying on automated advertising work. The article explains the affected areas and provides a local review aid for exported settings, helping identify where existing workflows may need adjustment without changing campaigns during the review.
Contents
Meta released Graph API and Marketing API v26.0 on 29 July 2026. Several changes also reach older supported Marketing API versions on 27 October 2026. A version pin means that requests explicitly select a fixed API version. It therefore needs a second check: the effective date of each individual change. This article covers six changes relevant to ad automation; the full release contains additional changes.
1. Version scope and date are separate checks
Meta documents both version-specific changes and changes that apply across versions. The latter are part of the documented release process, rather than a new guarantee being broken. The Marketing API and Graph API also have different retirement schedules. For example, the Graph API v25.0 end date must not be used as the retirement date for Marketing API v25.0.
As checked on 24 October 2026, Marketing API v24.0 had already reached its listed end date of 6 October 2026. Remaining on an unsupported version is not a migration strategy. The current version table and the individual change notices both belong in the integration inventory.
2. Six changes and their actual scope
A creative is the ad’s content, such as its image and text. An ad set groups ads under shared delivery settings. A placement is a display location, such as a feed or story.
- Delivery estimates:
daily_outcomes_curve,budget_guardrailandestimate_daudisappear from delivery-estimate responses in v26.0 and from the remaining supported versions on 27 October. Meta lists no replacement. Both account-level and ad-set-level delivery-estimate reads and downstream calculations need checking. - Web + App campaigns: a creative with
applink_treatment=web_onlycan no longer be attached to an ad in a campaign using Website and App conversion locations. This is not a general ban on website destinations. The documented alternative is automatic routing or a compatible manual configuration. Scope: v26.0, then all supported Marketing API versions on 27 October. - Poll ads:
poll_specand poll components underinteractive_components_specare affected when creating or changing ads and creatives. Updates that leave the creative unchanged can continue. Scope: v26.0, then the remaining supported versions on 27 October. - Messenger Stories:
storyis silently removed frommessenger_positions. Historical targeting can still contain the value. Manual placements need adjustment; Advantage+ Placements require no action for this removal. Scope: v26.0, then all supported Marketing API versions, including unversioned calls, on 27 October. - Instagram Explore Feed: explicitly requesting this placement causes an error from v26.0 onward. The notice does not assign the same all-version deadline of 27 October. Placement values must be checked against the actual request and the relevant placement documentation, not inferred from a display label.
- Advantage+ audience: creating certain non-broad Housing, Employment or Financial Products and Services ad sets with relaxable targeting requires an explicit
targeting_automation.advantage_audiencevalue of 1 or 0. Broad/default configurations are not generally affected. The requirement starts in v26.0; expansion to all versions follows retirement of Marketing API v25.0, whose date was still unspecified at the check date.
A successful HTTP response cannot prove that the requested placement mix survived unchanged. Delivery configuration and effective placements need comparison as well as error monitoring. The magnitude of any performance effect depends on the account and cannot be inferred from the changelog.

3. A local script for review hints
JSON stores structured data as named fields and lists. The following Python script scans exported objects and arrays recursively: it also visits nested fields and lists. It finds explicit references to removed fields, Messenger Stories, Instagram placements, web-only routing and potentially relevant creative structures. A match is a review hint, not proof that an API request will fail. For example, an interactive component need not be a poll, and a web-only creative needs campaign context.
The script deliberately does not infer special-ad-category rules from a made-up vertical field. Campaign category, broad versus non-broad targeting, relaxability, API version and operation belong in the separate manual check. JSON embedded inside strings also needs decoding before scanning. Only files directly inside the selected directory are covered.
#!/usr/bin/env python3
"""Local review hints only; no API calls and no automatic fixes."""
import json
import pathlib
import re
import sys
REMOVED = {"daily_outcomes_curve", "budget_guardrail", "estimate_dau"}
def scan(node, path="$", found=None):
found = [] if found is None else found
if isinstance(node, dict):
for key, value in node.items():
here = f"{path}.{key}"
if key in REMOVED:
found.append((here, "REMOVED_FIELD_REFERENCE", key))
if key == "fields":
# Flat field lists only; nested Graph field syntax needs review.
fields = value.split(",") if isinstance(value, str) else value
if isinstance(fields, list):
for field in fields:
if isinstance(field, str):
for token in re.findall(r"[A-Za-z_][A-Za-z_0-9]*", field):
if token in REMOVED:
found.append((here, "REMOVED_FIELD_REQUEST", token))
else:
found.append((here, "INVALID_FIELDS_SHAPE", "review"))
if key == "messenger_positions":
if not isinstance(value, list):
found.append((here, "INVALID_PLACEMENT_SHAPE", "review"))
elif "story" in value:
found.append((here, "MESSENGER_STORY", "review manual placement"))
if key == "instagram_positions":
found.append((here, "INSTAGRAM_PLACEMENT_REVIEW", "check Explore Feed mapping"))
if key == "applink_treatment" and value == "web_only":
found.append((here, "WEB_APP_CONTEXT_REVIEW", "check campaign conversion locations"))
if key in {"poll_spec", "interactive_components_spec"}:
found.append((here, "CREATIVE_REVIEW", "check poll use and operation"))
scan(value, here, found)
elif isinstance(node, list):
for index, value in enumerate(node):
scan(value, f"{path}[{index}]", found)
return found
def main(directory):
files = sorted(pathlib.Path(directory).glob("*.json"))
if not files:
print("INPUT_ERROR: no JSON files", file=sys.stderr)
return 2
findings, errors = 0, 0
for file in files:
try:
node = json.loads(file.read_text(encoding="utf-8"))
if not isinstance(node, (dict, list)):
raise ValueError("expected JSON object or array")
except (OSError, ValueError) as exc:
print(json.dumps({"file": file.name, "error": type(exc).__name__}))
errors += 1
continue
for path, kind, detail in scan(node):
print(json.dumps({"file": file.name, "path": path, "kind": kind, "detail": detail}))
findings += 1
print(json.dumps({"files": len(files), "review_hints": findings, "input_errors": errors}))
return 2 if errors else (1 if findings else 0)
if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("usage: audit_v26.py DIRECTORY")
raise SystemExit(main(sys.argv[1]))Invocation: python3 audit_v26.py /srv/adops/adset_configs. Exit 1 means review hints, exit 2 means an input error, and exit 0 only means no hints from these checks. An empty directory is an error. The deployment job must preserve the exit code; appending a successful || echo would hide it. File paths and configuration fragments may reveal internal structure, so audit output belongs in access-controlled logs.
4. Completing the inventory and verifying changes
- The inventory records the API version, endpoint (request address), operation and owner for each integration. It includes stored templates, software using the reports and ads created in the interface.
- A read-only account inventory connects ads, creatives, ad sets and campaigns. A local export alone cannot cover later interface changes.
- Each finding is compared with the scope above. The resulting changes remove obsolete manual placements and replace affected Web + App routing. The precise audience case is checked before a setting is changed.
- Removed delivery fields are followed through calculations and dashboards. A nullable column accepts missing values. Such a column can remain present while concealing a lost metric; zero is not a substitute for missing data.
- Approved changes are validated in a controlled test setup; responses and effective configuration are compared around the deadline. Creating or delivering test ads can incur spend; a local scan itself makes no API calls.
The complete v26.0 changelog also includes legacy protocol changes, Page and Commerce changes, and additional Marketing API features. Six checks cannot certify the whole integration.
5. Separate checks of geography changes
The DMA-to-Comscore transition is separate from the October deadline. Supermetrics documents its own Facebook Ads connector change: the former DMA dimension stops returning results from 22 June 2026 and is replaced by Comscore market. That is evidence for this connector, not proof that every warehouse has lost rows or that every Meta endpoint changed identically.
The following diagnostic finds measurement records without a matching area in an example old lookup table. NOT EXISTS prevents duplicate dimension rows from multiplying impressions. A null geographic name also remains unmatched. Table and column names are illustrative; this query measures mapping gaps, not the cause of a platform migration.
SELECT f.report_date, f.geo_name, SUM(f.impressions) AS impressions
FROM meta_geo_daily AS f
WHERE f.report_date >= '2026-06-01'
AND NOT EXISTS (
SELECT 1 FROM dim_dma AS d
WHERE d.dma_name = f.geo_name
AND d.dma_code IS NOT NULL
)
GROUP BY f.report_date, f.geo_name
ORDER BY impressions DESC
LIMIT 25;The share of unmatched impressions in the total also matters, beyond the top 25 rows. An impression is one display of an ad. Production mappings should use documented geographic identifiers, the geographic system and validity dates instead of display names alone. Historical reprocessing depends on retained raw data and the available mapping. Crosswalks are tables linking the old and new geographic identifiers. They can support defined comparisons, but unequal boundaries need explicit qualifications.
Questions and answers
Does a clean script result establish readiness?
No. Missing exports, campaign context, unsupported versions, string-encoded JSON and changes outside the six checks remain outside this scan. Read-only account inventory, a documented manual decision for each contextual finding and controlled verification are still required.
Does pinning to v25.0 avoid the October deadline?
Not for the four changes with an explicit cross-version deadline: delivery fields, the affected Web + App routing, poll ads and Messenger Stories. Explore Feed and the special Advantage+ audience requirement have different version rules.