Files
adsPRO/tools/meta-ads-active-insights.php
Jacek Pyziak b54a9a71b1 Add CLI script to fetch active Meta Ads insights for campaigns, adsets, and ads
- Implemented a new PHP script to retrieve insights for the last N days (default 30).
- Supports command-line options for token, account ID, days, API version, and output file.
- Fetches data at campaign, adset, and ad levels, with filtering for active statuses.
- Handles JSON output and optional file saving, including directory creation if necessary.
- Includes error handling for cURL requests and JSON responses.
2026-02-20 23:45:36 +01:00

258 lines
6.7 KiB
PHP

<?php
/**
* Simple CLI script to fetch active Meta Ads insights for:
* campaign, adset, ad levels for the last N days (default 30).
*
* Usage:
* php tools/meta-ads-active-insights.php --token=TOKEN --account=act_123456789 --days=30
* php tools/meta-ads-active-insights.php --token=TOKEN --account=123456789 --output=tmp/meta_insights.json
*
* You can also use env vars:
* META_ACCESS_TOKEN
* META_AD_ACCOUNT_ID
*/
if ( PHP_SAPI !== 'cli' )
{
fwrite( STDERR, "This script must be run from CLI.\n" );
exit( 1 );
}
$options = getopt( '', [
'token::',
'account::',
'days::',
'api-version::',
'output::',
'help::'
] );
if ( isset( $options['help'] ) )
{
echo "Usage:\n";
echo " php tools/meta-ads-active-insights.php --token=TOKEN --account=act_123 --days=30\n";
echo " php tools/meta-ads-active-insights.php --token=TOKEN --account=123 --output=tmp/meta.json\n";
echo "\n";
echo "Options:\n";
echo " --token Meta access token (or META_ACCESS_TOKEN env)\n";
echo " --account Ad account id, with or without act_ prefix (or META_AD_ACCOUNT_ID env)\n";
echo " --days Number of days back, default 30\n";
echo " --api-version Graph API version, default v25.0\n";
echo " --output Optional output JSON file path\n";
exit( 0 );
}
$token = trim( (string) ( $options['token'] ?? getenv( 'META_ACCESS_TOKEN' ) ?? '' ) );
$account_id = trim( (string) ( $options['account'] ?? getenv( 'META_AD_ACCOUNT_ID' ) ?? '' ) );
$days = (int) ( $options['days'] ?? 30 );
$api_version = trim( (string) ( $options['api-version'] ?? 'v25.0' ) );
$output = trim( (string) ( $options['output'] ?? '' ) );
if ( $token === '' )
{
fwrite( STDERR, "Missing token. Use --token or META_ACCESS_TOKEN.\n" );
exit( 1 );
}
if ( $account_id === '' )
{
fwrite( STDERR, "Missing ad account id. Use --account or META_AD_ACCOUNT_ID.\n" );
exit( 1 );
}
if ( strpos( $account_id, 'act_' ) !== 0 )
{
$account_id = 'act_' . preg_replace( '/\D+/', '', $account_id );
}
if ( $account_id === 'act_' )
{
fwrite( STDERR, "Invalid ad account id.\n" );
exit( 1 );
}
if ( $days < 1 )
{
$days = 1;
}
$since = date( 'Y-m-d', strtotime( '-' . ( $days - 1 ) . ' days' ) );
$until = date( 'Y-m-d' );
$base_url = 'https://graph.facebook.com/' . rawurlencode( $api_version ) . '/' . rawurlencode( $account_id ) . '/insights';
$levels = [
'campaign' => [
'fields' => 'account_id,campaign_id,campaign_name,spend,impressions,clicks,ctr,cpc,action_values,purchase_roas,date_start,date_stop',
'filtering_field' => 'campaign.effective_status'
],
'adset' => [
'fields' => 'account_id,campaign_id,campaign_name,adset_id,adset_name,spend,impressions,clicks,ctr,cpc,action_values,purchase_roas,date_start,date_stop',
'filtering_field' => 'adset.effective_status'
],
'ad' => [
'fields' => 'account_id,campaign_id,campaign_name,adset_id,adset_name,ad_id,ad_name,spend,impressions,clicks,ctr,cpc,action_values,purchase_roas,date_start,date_stop',
'filtering_field' => 'ad.effective_status'
]
];
$result = [
'meta' => [
'account_id' => $account_id,
'api_version' => $api_version,
'days' => $days,
'since' => $since,
'until' => $until,
'generated_at' => date( 'c' )
],
'campaign' => [],
'adset' => [],
'ad' => [],
'summary' => []
];
try
{
foreach ( $levels as $level => $cfg )
{
fwrite( STDERR, "Fetching level: {$level}\n" );
$params = [
'access_token' => $token,
'level' => $level,
'fields' => $cfg['fields'],
'time_increment' => 1,
'time_range' => json_encode( [ 'since' => $since, 'until' => $until ] ),
'filtering' => json_encode( [
[
'field' => $cfg['filtering_field'],
'operator' => 'IN',
'value' => [ 'ACTIVE' ]
]
] ),
'limit' => 500
];
$rows = fetch_all_pages( $base_url, $params );
$result[ $level ] = $rows;
$result['summary'][ $level . '_rows' ] = count( $rows );
}
}
catch ( Exception $e )
{
fwrite( STDERR, "Error: " . $e -> getMessage() . "\n" );
exit( 1 );
}
$json = json_encode( $result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT );
if ( $json === false )
{
fwrite( STDERR, "Failed to encode JSON output.\n" );
exit( 1 );
}
if ( $output !== '' )
{
$output_dir = dirname( $output );
if ( $output_dir !== '' && $output_dir !== '.' && !is_dir( $output_dir ) )
{
if ( !mkdir( $output_dir, 0775, true ) && !is_dir( $output_dir ) )
{
fwrite( STDERR, "Failed to create output directory: {$output_dir}\n" );
exit( 1 );
}
}
if ( file_put_contents( $output, $json ) === false )
{
fwrite( STDERR, "Failed to write output file: {$output}\n" );
exit( 1 );
}
fwrite( STDERR, "Saved: {$output}\n" );
}
else
{
echo $json . PHP_EOL;
}
exit( 0 );
function fetch_all_pages( $url, $params = null )
{
$all_rows = [];
$next_url = $url;
$next_params = $params;
while ( $next_url )
{
$payload = request_json( $next_url, $next_params );
if ( isset( $payload['data'] ) && is_array( $payload['data'] ) )
{
foreach ( $payload['data'] as $row )
{
$all_rows[] = $row;
}
}
$next_url = '';
$next_params = null;
if ( isset( $payload['paging']['next'] ) && is_string( $payload['paging']['next'] ) )
{
$next_url = $payload['paging']['next'];
}
}
return $all_rows;
}
function request_json( $url, $params = null )
{
if ( is_array( $params ) )
{
$query = http_build_query( $params );
$url .= ( strpos( $url, '?' ) === false ? '?' : '&' ) . $query;
}
$ch = curl_init( $url );
curl_setopt_array( $ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CONNECTTIMEOUT => 20,
CURLOPT_TIMEOUT => 120
] );
$response = curl_exec( $ch );
$curl_error = curl_error( $ch );
$http_code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
curl_close( $ch );
if ( $response === false )
{
throw new Exception( 'cURL error: ' . $curl_error );
}
$decoded = json_decode( $response, true );
if ( !is_array( $decoded ) )
{
throw new Exception( 'Invalid JSON response. HTTP ' . $http_code . '. Body: ' . substr( (string) $response, 0, 1000 ) );
}
if ( isset( $decoded['error'] ) )
{
$message = (string) ( $decoded['error']['message'] ?? 'Unknown API error' );
$code = (string) ( $decoded['error']['code'] ?? '' );
$subcode = (string) ( $decoded['error']['error_subcode'] ?? '' );
throw new Exception( 'Meta API error: ' . $message . ' (code: ' . $code . ', subcode: ' . $subcode . ')' );
}
if ( $http_code >= 400 )
{
throw new Exception( 'HTTP error ' . $http_code . '. Body: ' . substr( (string) $response, 0, 1000 ) );
}
return $decoded;
}