Class: Marketing::AdSpend::OpenaiAdapter

Inherits:
Object
  • Object
show all
Defined in:
app/services/marketing/ad_spend/openai_adapter.rb

Overview

Daily campaign spend from OpenAI (ChatGPT) Ads.

Insights are per-campaign and the endpoint ignores date params, so this
fans out over the campaigns and filters to the requested day client-side —
see OpenaiAds::AdvertiserApiClient#campaign_insights for the API's traps.
Volume is tiny (single-digit campaigns), so the per-campaign fan-out costs
nothing; revisit if the account grows into the hundreds.

Constant Summary collapse

PROVIDER =

Provider key recorded on ad spend rows produced by this adapter.

'openai_ads'

Class Method Summary collapse

Class Method Details

.call(date) ⇒ Array<Hash>

Returns { external_id:, spend:, clicks:, impressions:, conversions: }
empty when no API key is configured. conversions is always nil — the
Ads API exposes no conversions field.

Parameters:

  • date (Date)

Returns:

  • (Array<Hash>)

    { external_id:, spend:, clicks:, impressions:, conversions: }
    empty when no API key is configured. conversions is always nil — the
    Ads API exposes no conversions field.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'app/services/marketing/ad_spend/openai_adapter.rb', line 18

def self.call(date)
  token = Heatwave::Configuration.fetch(:openai_ads, :advertiser_api_key)
  return [] if token.blank?

  client = OpenaiAds::AdvertiserApiClient.new
  wanted = date.to_s

  client.list_campaigns(token: token).flat_map do |campaign|
    client.campaign_insights(token: token, campaign_id: campaign['id'])
          .select { |row| row['readable_time'] == wanted }
          .map do |row|
            {
              external_id: row['campaign_id'].to_s,
              spend: row['spend'].to_f, # already dollars, not micros
              clicks: row['clicks'].to_i,
              impressions: row['impressions'].to_i,
              conversions: nil
            }
          end
  end
end