googleads-rs
Current Version 23.2.0 uses Google Ads API v23.2
Versioning Convention
The crate version directly corresponds to the Google Ads API version it supports.
- major.minor = Google Ads API version (e.g.,
23.2.0→ API v23.2)- patch = bug fixes and library updates
This eliminates the mental friction of mapping between the
googleads-rsversion and the Google Ads API version.
A gRPC client library for Google Ads API, generated automatically from the API definition files.
I use it for my mcc-gaql command line tool to run Google Ads Query Language queries across large number of MCC-linked accounts.
The GoogleAdsRow.get(path: &str) accessor method uses prost-reflect for dynamic field access, allowing you to retrieve any field from the protobuf message by its GAQL path without hardcoding match arms for each field.
Example
let client: GoogleAdsServiceClient<InterceptedService<Channel, GoogleAdsAPIAccess>> =
GoogleAdsServiceClient::with_interceptor(api_context.channel.clone(), api_context);
let result: Result<Response<Streaming<SearchGoogleAdsStreamResponse>>, Status> = client
.search_stream(SearchGoogleAdsStreamRequest {
customer_id: customer_id.clone(),
query,
summary_row_setting: 0,
})
.await;
match result {
Ok(response) => {
let mut stream = response.into_inner();
let mut columns: Vec<Vec<String>> = Vec::new();
let mut headers: Option<Vec<String>> = None;
while let Some(item) = stream.next().await {
match item {
Ok(stream_response) => {
let field_mask = stream_response.field_mask.unwrap();
if headers.is_none() {
headers = Some(field_mask.paths.clone());
}
for r in stream_response.results {
let row: GoogleAdsRow = r;
// go through all columns specified in query, pull out string value, and insert into columns
for i in 0..headers.as_ref().unwrap().len() {
let path = &headers.as_ref().unwrap()[i];
let string_val: String = row.get(path).trim_matches('"').to_string();
match columns.get_mut(i) {
Some(v) => {
v.push(string_val);
}
None => {
let v: Vec<String> = vec![string_val];
columns.insert(i, v);
}
}
}
}
}
Err(status) => {
bail!(
"GoogleAdsClient streaming error. Account: {customer_id}, Message: {}, Details: {}",
status.message(),
String::from_utf8_lossy(status.details()).into_owned()
);
}
}
}
}
}
API Upgrade
Run update.sh to update the library for a new Google Ads API version:
- Download latest proto files for new Google Ads API version
- Replace references to old api version in build.rs, lib.rs, and README.md
./utils/update.sh v17
Build process
- build.rs dynamically scans for available proto files, filters them, and feeds them to tonic to generate
protos.rs(following strategy by aquarhead) - build.rs also generates a file descriptor set using
prost-reflect'scompile_fds()for runtime reflection - lib.rs includes
protos.rs - lib.rs implements
get()andget_many()usingprost-reflectfor dynamic field access:
The get() method uses prost-reflect to:
- Encode the
GoogleAdsRowto protobuf bytes - Decode it as a
DynamicMessageusing the descriptor pool loaded at startup - Walk the dotted field path (e.g.,
"campaign.id") recursively through the message structure - Format the value appropriately (scalars, enums, nested messages, repeated fields)
This approach supports any field in the Google Ads API without manual match arms, including:
- Scalar fields (strings, numbers, booleans)
- Enum fields (resolved to their name)
- Nested messages (automatically traversed)
- Repeated fields (formatted as comma-separated lists)
- Special cases like
campaign.asset_automation_settingsandresponsive_search_adheadlines/descriptions
- cargo test may fail due to compilation errors with example code in comment blocks of proto files. enclose them in ignore blocks.
// A message representing the message types used by a long-running operation.
// ```ignore
// Example:
//
// rpc Export(ExportRequest) returns (google.longrunning.Operation) {
// option (google.longrunning.operation_info) = {
// response_type: "ExportResponse"
// metadata_type: "ExportMetadata"
// };
// }
// ```
Documentation
- to generate docs quicker, exclude dependent crates
$$ cargo doc --no-deps --open
Credits
- Originally forked from gkkachi's gapi-grpc-rs, which used Python to generate
protos.rs - Dropped Python and migrated to custom build.rs per aquarhead