Prácticas recomendadas

En esta página, se describen varias prácticas recomendadas para desarrollar apps con las secuencias de comandos de Google Ads.

Selectores

Filtra con selectores

Cuando sea posible, usa filtros para solicitar solo las entidades que necesitas. Aplicar los filtros adecuados tiene los siguientes beneficios:

  • El código es más simple y fácil de entender.
  • La secuencia de comandos se ejecutará mucho más rápido.

Compara los siguientes fragmentos de código:

Enfoque de programación Fragmento de código
Cómo filtrar con selectores (recomendado)
var keywords = AdsApp.keywords()
    .withCondition('Clicks > 10')
    .forDateRange('LAST_MONTH')
    .get();
while (keywords.hasNext()) {
  var keyword = keywords.next();
  // Do work here.
}
Filtra el código (no se recomienda)
var keywords = AdsApp.keywords().get();

while (keywords.hasNext()) {
  var keyword = keywords.next();
  var stats = keyword.getStatsFor(
      'LAST_MONTH');
  if (stats.getClicks() > 10) {
    // Do work here.
  }
}

El segundo enfoque no se recomienda porque intenta recuperar la lista de todas las palabras clave de tu cuenta solo para aplicar un filtro a la lista.

Evitar recorrer la jerarquía de la campaña

Cuando desees recuperar entidades en un nivel determinado, utiliza un método de recopilación en ese nivel, en lugar de recorrer toda la jerarquía de la campaña. Además de ser más simple, esto también ofrecerá un mejor rendimiento: el sistema no tendrá que leer innecesariamente todas las campañas y todos los grupos de anuncios.

Compara los siguientes fragmentos de código que recuperan todos los anuncios en tu cuenta:

Enfoque de programación Fragmento de código
Usa el método de recopilación adecuado (recomendado)

var ads = AdsApp.ads();

Desvía la jerarquía (no recomendado)
var campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
  var adGroups = campaigns.next().
      adGroups().get();
  while (adGroups.hasNext()) {
    var ads = adGroups.next().ads().get();
    // Do your work here.
  }
}

No se recomienda el segundo enfoque, ya que intenta recuperar jerarquías completas de objetos (campañas, grupos de anuncios), mientras que solo se requieren anuncios.

Usa métodos específicos del descriptor de acceso superior

A veces, debes obtener la entidad principal de un objeto recuperado. En este caso, debes usar un método de acceso proporcionado en lugar de recuperar jerarquías completas.

Compara los siguientes fragmentos de código que recuperan los grupos de anuncios que tienen anuncios de texto con más de 50 clics el mes pasado:

Enfoque de programación Fragmento de código
Usa el método apropiado para el descriptor de acceso de los padres (recomendado)
var ads = AdsApp.ads()
    .withCondition('Clicks > 50')
    .forDateRange('LAST_MONTH')
    .get();

while (ads.hasNext()) {
  var ad = ads.next();
  var adGroup = ad.getAdGroup();
  var campaign = ad.getCampaign();
  // Store (campaign, adGroup) to an array.
}
Recorrer la jerarquía (no recomendado)
var campaigns = AdsApp.campaigns().get();
while (campaigns.hasNext()) {
  var adGroups = campaigns.next()
      .adGroups()
      .get();
  while (adGroups.hasNext()) {
    var ads = adGroups.ads()
       .withCondition('Clicks > 50')
       .forDateRange('LAST_MONTH')
       .get();
    if (ads.totalNumEntities() > 0) {
      // Store (campaign, adGroup) to an array.
    }
  }
}

No se recomienda el segundo enfoque, ya que recupera todas las jerarquías de la campaña y del grupo de anuncios de tu cuenta, mientras que solo necesitas un subconjunto de campañas y grupos de anuncios asociado con tu conjunto de anuncios. El primer enfoque se restringe para recuperar solo la colección de anuncios relevante y usa un método adecuado para acceder a sus objetos superiores.

Usa filtros superiores específicos

Para acceder a entidades dentro de una campaña o un grupo de anuncios específicos, utiliza un filtro específico en el selector en lugar de recuperar y luego recorrer una jerarquía.

Compara los siguientes fragmentos de código que recuperan la lista de anuncios de texto dentro de una campaña y un grupo de anuncios especificados que recibieron más de 50 clics el mes pasado.

Enfoque de programación Fragmento de código
Usa los filtros adecuados de nivel superior (recomendado)
var ads = AdsApp.ads()
    .withCondition('CampaignName = "Campaign 1"')
    .withCondition('AdGroupName = "AdGroup 1"')
    .withCondition('Clicks > 50')
    .forDateRange('LAST_MONTH')
    .get();

while (ads.hasNext()) {
  var ad = ads.next();
  var adGroup = ad.getAdGroup();
  var campaign = ad.getCampaign();
  // Store (campaign, adGroup, ad) to
  // an array.
}
Recorrer la jerarquía (no recomendado)
var campaigns = AdsApp.campaigns()
    .withCondition('Name = "Campaign 1"')
    .get();

while (campaigns.hasNext()) {
  var adGroups = campaigns.next()
      .adGroups()
      .withCondition('Name = "AdGroup 1"')
      .get();
  while (adGroups.hasNext()) {
    var ads = adGroups.ads()
       .withCondition('Clicks > 50')
       .forDateRange('LAST_MONTH')
       .get();
    while (ads.hasNext()) {
      var ad = ads.next();
      // Store (campaign, adGroup, ad) to
      // an array.
    }
  }
}

No se recomienda el segundo enfoque, ya que itera en la jerarquía de las campañas y los grupos de anuncios en tu cuenta, mientras que solo necesitas un conjunto seleccionado de anuncios y sus campañas y grupos de anuncios superiores. El primer enfoque limita la iteración a la lista de anuncios mediante la aplicación de un filtro específico para las entidades principales en el selector.

Usa los IDs para filtrar cuando sea posible

Cuando se filtra por entidades, es preferible filtrar las entidades por su ID en lugar de otros campos.

Considera los siguientes fragmentos de código para seleccionar una campaña.

Enfoque de programación Fragmento de código
Filtrar por ID (recomendado)
var campaign = AdsApp.campaigns()
    .withIds([12345])
    .get()
    .next();
Filtrar por nombre (menos óptimo)
var campaign = AdsApp.campaigns()
    .withCondition('Name="foo"')
    .get()
    .next();

El segundo enfoque es menos óptimo, ya que filtramos por un campo que no sea ID.

Filtra por IDs parentales siempre que sea posible

Cuando selecciones una entidad, filtra por IDs principales siempre que sea posible. Esto hará que tus consultas sean más rápidas, ya que limitará la lista de entidades que los servidores recuperan cuando se filtran los resultados.

Considera el siguiente fragmento de código que recupera un grupo de anuncios por su ID. Supongamos que el ID de la campaña principal es conocido.

Enfoque de programación Fragmento de código
Filtrar por IDs de campañas y grupos de anuncios (recomendado)
var adGroup = AdsApp.adGroups()
    .withIds([12345])
    .withCondition('CampaignId="54678"')
    .get()
    .next();
Filtrar solo por ID del grupo de anuncios (menos óptimo)
var adGroup = AdsApp.adGroups()
    .withIds([12345])
    .get()
    .next();

Aunque ambos fragmentos de código dan resultados idénticos, el filtrado adicional en el fragmento de código 1 con un ID superior (CampaignId="54678") hace que el código sea más eficiente, ya que restringe la lista de entidades que el servidor debe iterar cuando filtra los resultados.

Usar etiquetas cuando haya demasiadas condiciones de filtrado

Cuando tienes demasiadas condiciones de filtrado, es una buena idea crear una etiqueta para las entidades que procesas y usarla para filtrar las entidades.

Considera el siguiente fragmento de código que recupera una lista de campañas por su nombre.

Enfoque de programación Fragmento de código
Usar una etiqueta (recomendado)
var label = AdsApp.labels()
    .withCondition('Name = "My Label"')
    .get()
    .next();
var campaigns = label.campaigns.get();
while (campaigns.hasNext()) {
  var campaign = campaigns.next();
  // Do more work
}
Compila selectores complejos (no recomendado)
var campaignNames = [‘foo’, ‘bar’, ‘baz’];

for (var i = 0; i < campaignNames.length; i++) {
  campaignNames[i] = '"' + campaignNames[i] + '"';
}

var campaigns = AdsApp.campaigns
    .withCondition('CampaignName in [' + campaignNames.join(',') + ']')
    .get();

while (campaigns.hasNext()) {
  var campaign = campaigns.next();
  // Do more work.
}

Si bien ambos fragmentos de código te proporcionan un nivel de rendimiento similar, el segundo enfoque tiende a generar código más complejo a medida que aumenta la cantidad de condiciones en el selector. También es más fácil aplicar la etiqueta a una entidad nueva que editar la secuencia de comandos para incluir una entidad nueva.

Limita el número de condiciones en tu cláusula IN

Cuando se ejecutan secuencias de comandos, un caso de uso común es ejecutar un informe para una lista de entidades. Por lo general, los desarrolladores logran esto mediante la creación de una consulta de AWQL muy larga que filtra los ID de la entidad con una cláusula IN. Este enfoque funciona bien cuando la cantidad de entidades es limitada. Sin embargo, a medida que aumenta la longitud de tu consulta, el rendimiento de la secuencia de comandos se deteriora debido a dos motivos:

  • Una consulta más larga tarda más en analizarse.
  • Cada ID que agregas a una cláusula IN es una condición adicional para evaluar y, por lo tanto, lleva más tiempo.

En esas condiciones, es preferible aplicar una etiqueta a las entidades y, luego, filtrar por LabelId.

Enfoque de programación Fragmento de código
Cómo aplicar una etiqueta y filtrar por labelID (recomendado)
// The label applied to the entity is "Report Entities"
var label = AdsApp.labels()
    .withCondition('LabelName contains "Report Entities"')
    .get()
    .next();

var report = AdsApp.report('SELECT AdGroupId, Id, Clicks, ' +
    'Impressions, Cost FROM KEYWORDS_PERFORMANCE_REPORT ' +
    'WHERE LabelId = "' + label.getId() + '"');
Crea una consulta larga con la cláusula IN (no recomendado)
var report = AdsApp.report('SELECT AdGroupId, Id, Clicks, ' +
    'Impressions, Cost FROM KEYWORDS_PERFORMANCE_REPORT WHERE ' +
    'AdGroupId IN (123, 456) and Id in (123,345, 456…)');

Actualizaciones de la cuenta

Cambios por lotes

Cuando realizas cambios en una entidad de Google Ads, las secuencias de comandos de Google Ads no ejecutan el cambio de inmediato. En cambio, intenta combinar varios cambios en lotes para poder emitir una sola solicitud que realice varios cambios. Este enfoque hace que tus secuencias de comandos sean más rápidas y reduce la carga en los servidores de Google Ads. Sin embargo, hay algunos patrones de código que obligan a las secuencias de comandos de Google Ads a vaciar el lote de operaciones con frecuencia, lo que provoca que la secuencia de comandos se ejecute lentamente.

Considera la siguiente secuencia de comandos que actualiza las ofertas de una lista de palabras clave.

Enfoque de programación Fragmento de código
Realizar un seguimiento de los elementos actualizados (recomendado)
var keywords = AdsApp.keywords()
    .withCondition('Clicks > 50')
    .withCondition('CampaignName = "Campaign 1"')
    .withCondition('AdGroupName = "AdGroup 1"')
    .forDateRange('LAST_MONTH')
    .get();

var list = [];
while (keywords.hasNext()) {
  var keyword = keywords.next();
  keyword.bidding().setCpc(1.5);
  list.push(keyword);
}

for (var i = 0; i < list.length; i++) {
  var keyword = list[i];
  Logger.log('%s, %s', keyword.getText(),
      keyword.bidding().getCpc());
}
Recupera los elementos actualizados en un bucle cerrado (no recomendado)
var keywords = AdsApp.keywords()
    .withCondition('Clicks > 50')
    .withCondition('CampaignName = "Campaign 1"')
    .withCondition('AdGroupName = "AdGroup 1"')
    .forDateRange('LAST_MONTH')
    .get();

while (keywords.hasNext()) {
  var keyword = keywords.next();
  keyword.bidding().setCpc(1.5);
  Logger.log('%s, %s', keyword.getText(),
      keyword.bidding().getCpc());
}

No se recomienda el segundo enfoque, ya que la llamada a keyword.bidding().getCpc() obliga a las secuencias de comandos de Google Ads a vaciar la operación setCpc() y ejecutar solo una operación a la vez. El primer enfoque, aunque es similar al segundo, tiene el beneficio adicional de admitir el procesamiento por lotes, ya que la llamada a getCpc() se realiza en un bucle separado del que se llama a setCpc().

Usa compiladores cuando sea posible

Las secuencias de comandos de Google Ads admiten dos formas de crear objetos nuevos: compiladores y métodos de creación. Los compiladores son más flexibles que los métodos de creación, ya que te brindan acceso al objeto que se crea desde la llamada a la API.

Considera los siguientes fragmentos de código:

Enfoque de programación Fragmento de código
Usa compiladores (recomendado)
var operation = adGroup.newKeywordBuilder()
    .withText('shoes')
    .build();
var keyword = operation.getResult();
Usa métodos de creación (no recomendado)
adGroup.createKeyword('shoes');
var keyword = adGroup.keywords()
    .withCondition('KeywordText="shoes"')
    .get()
    .next();

No se prefiere el segundo enfoque debido a la operación de selección adicional involucrada en la recuperación de la palabra clave. Además, los métodos de creación también están obsoletos.

Sin embargo, ten en cuenta que los compiladores, cuando se usan de forma incorrecta, pueden evitar que las secuencias de comandos de Google Ads agrupen sus operaciones en lotes.

Considera los siguientes fragmentos de código que crean una lista de palabras clave y, además, imprimen el ID de las palabras clave recién creadas:

Enfoque de programación Fragmento de código
Realizar un seguimiento de los elementos actualizados (recomendado)
var keywords = [‘foo’, ‘bar’, ‘baz’];

var list = [];
for (var i = 0; i < keywords.length; i++) {
  var operation = adGroup.newKeywordBuilder()
      .withText(keywords[i])
      .build();
  list.push(operation);
}

for (var i = 0; i < list.length; i++) {
  var operation = list[i];
  var result = operation.getResult();
  Logger.log('%s %s', result.getId(),
      result.getText());
}
Recupera los elementos actualizados en un bucle cerrado (no recomendado)
var keywords = [‘foo’, ‘bar’, ‘baz’];

for (var i = 0; i < keywords.length; i++) {
  var operation = adGroup.newKeywordBuilder()
      .withText(keywords[i])
      .build();
  var result = operation.getResult();
  Logger.log('%s %s', result.getId(),
      result.getText());
}

No se recomienda el segundo enfoque porque llama a operation.getResult() dentro del mismo bucle que crea la operación, lo que obliga a las secuencias de comandos de Google Ads a ejecutar una operación a la vez. El primer enfoque, aunque similar, permite el procesamiento por lotes, ya que llamamos a operations.getResult() en un bucle diferente al donde se creó.

Considera usar cargas masivas para las actualizaciones a gran escala.

Una tarea común que realizan los desarrolladores es ejecutar informes y actualizar las propiedades de la entidad (por ejemplo, las ofertas de palabras clave) según los valores de rendimiento actuales. Cuando tienes que actualizar una gran cantidad de entidades, las cargas masivas tienden a brindarte un mejor rendimiento. Por ejemplo, considera las siguientes secuencias de comandos que aumentan el MaxCpc de las palabras clave cuyo TopImpressionPercentage > 0.4 durante el último mes:

Enfoque de programación Fragmento de código
Utilizar la carga masiva (recomendado)

var report = AdsApp.report(
  'SELECT AdGroupId, Id, CpcBid FROM KEYWORDS_PERFORMANCE_REPORT ' +
  'WHERE TopImpressionPercentage > 0.4 DURING LAST_MONTH');

var upload = AdsApp.bulkUploads().newCsvUpload([
  report.getColumnHeader('AdGroupId').getBulkUploadColumnName(),
  report.getColumnHeader('Id').getBulkUploadColumnName(),
  report.getColumnHeader('CpcBid').getBulkUploadColumnName()]);
upload.forCampaignManagement();

var reportRows = report.rows();
while (reportRows.hasNext()) {
  var row = reportRows.next();
  row['CpcBid'] = row['CpcBid'] + 0.02;
  upload.append(row.formatForUpload());
}

upload.apply();
Seleccionar y actualizar palabras clave por ID (menos óptimo)
var reportRows = AdsApp.report('SELECT AdGroupId, Id, CpcBid FROM ' +
    'KEYWORDS_PERFORMANCE_REPORT WHERE TopImpressionPercentage > 0.4 ' +
    ' DURING LAST_MONTH')
    .rows();

var map = {
};

while (reportRows.hasNext()) {
  var row = reportRows.next();
  var adGroupId = row['AdGroupId'];
  var id = row['Id'];

  if (map[adGroupId] == null) {
    map[adGroupId] = [];
  }
  map[adGroupId].push([adGroupId, id]);
}

for (var key in map) {
  var keywords = AdsApp.keywords()
      .withCondition('AdGroupId="' + key + '"')
      .withIds(map[key])
      .get();

  while (keywords.hasNext()) {
    var keyword = keywords.next();
    keyword.bidding().setCpc(keyword.bidding().getCpc() + 0.02);
  }
}

Si bien el segundo enfoque brinda un rendimiento bastante bueno, en este caso, se prefiere el primero,

  • Las secuencias de comandos de Google Ads tienen un límite para la cantidad de objetos que se pueden recuperar o actualizar en una sola ejecución, y las operaciones de selección y actualización del segundo enfoque se tienen en cuenta para ese límite.

  • Las cargas masivas tienen límites más altos en cuanto a la cantidad de entidades que puede actualizar y el tiempo de ejecución general.

Agrupa tus cargas masivas por campañas

Cuando crees tus cargas masivas, intenta agrupar tus operaciones por campaña superior. Esto aumenta la eficiencia y disminuye la posibilidad de errores de simultaneidad o cambios conflictivos.

Considera dos tareas de carga masiva que se ejecuten en paralelo. Uno detiene los anuncios en un grupo de anuncios y el otro ajusta las ofertas de palabras clave. Aunque las operaciones no estén relacionadas, pueden aplicarse a entidades del mismo grupo de anuncios (o a dos grupos de anuncios diferentes en la misma campaña). Cuando esto sucede, el sistema bloquea la entidad principal (la campaña o el grupo de anuncios compartidos) y las tareas de carga masiva se bloquean entre sí.

Las secuencias de comandos de Google Ads pueden optimizar la ejecución dentro de una sola tarea de carga masiva. Por lo tanto, lo más simple es ejecutar solo una tarea de carga masiva por cuenta a la vez. Si decides ejecutar más de una carga masiva por cuenta, asegúrate de que las cargas masivas operen en listas de campañas mutuamente excluyentes (y sus entidades secundarias) para obtener un rendimiento óptimo.

Informes

Cómo usar los informes para recuperar estadísticas

Cuando deseas recuperar grandes cantidades de entidades y sus estadísticas, suele ser mejor usar informes en lugar de los métodos estándar de AdsApp. Se prefiere el uso de informes por los siguientes motivos:

  • Los informes te ofrecen un mejor rendimiento para las consultas grandes.
  • Los informes no alcanzarán las cuotas de recuperación normales.

Compara los siguientes fragmentos de código que recuperan los clics, las impresiones, el costo y el texto de todas las palabras clave que recibieron más de 50 clics el mes pasado:

Enfoque de programación Fragmento de código
Cómo usar los informes (recomendado)
  report = AdsApp.search(
      'SELECT ' +
      '   ad_group_criterion.keyword.text, ' +
      '   metrics.clicks, ' +
      '   metrics.cost_micros, ' +
      '   metrics.impressions ' +
      'FROM ' +
      '   keyword_view ' +
      'WHERE ' +
      '   segments.date DURING LAST_MONTH ' +
      '   AND metrics.clicks > 50');
  while (report.hasNext()) {
    var row = report.next();
    Logger.log('Keyword: %s Impressions: %s ' +
        'Clicks: %s Cost: %s',
        row.adGroupCriterion.keyword.text,
        row.metrics.impressions,
        row.metrics.clicks,
        row.metrics.cost);
  }
Usa iteradores de AdsApp (no recomendado)
var keywords = AdsApp.keywords()
    .withCondition('metrics.clicks > 50')
    .forDateRange('LAST_MONTH')
    .get();
while (keywords.hasNext()) {
  var keyword = keywords.next();
  var stats = keyword.getStatsFor('LAST_MONTH');
  Logger.log('Keyword: %s Impressions: %s ' +
      'Clicks: %s Cost: %s',
      keyword.getText(),
      stats.getImpressions(),
      stats.getClicks(),
      stats.getCost());
}

No se prefiere el segundo enfoque, ya que itera en las palabras clave y recupera las estadísticas de a una entidad a la vez. En este caso, los informes funcionan más rápido, ya que recuperan todos los datos en una sola llamada y los transmiten según sea necesario. Además, las palabras clave recuperadas en el segundo enfoque se cuentan en la cuota de tu secuencia de comandos para la cantidad de entidades recuperadas con una llamada get().

Usar la búsqueda en lugar del informe

El método de informes se compiló para la infraestructura anterior y generará los resultados en un formato plano, incluso si usas GAQL. Esto significa que debe transformar los resultados de la consulta para que coincidan con el estilo anterior, que no es compatible con todos los campos y que agrega sobrecarga a cada llamada.

En su lugar, le recomendamos que utilice la búsqueda para aprovechar todas las funciones de los informes de la nueva API de Google Ads.

Prefiero GAQL en lugar de AWQL.

Aunque AWQL todavía es compatible con las consultas de informes y las llamadas de withCondition, se ejecuta a través de una capa de traducción que no tiene compatibilidad total con AWQL real. Para tener control completo sobre tus consultas, asegúrate de usar GAQL.

Si tienes consultas de AWQL existentes que deseas traducir, contamos con una herramienta de migración de consultas para ayudarte.

No selecciones más filas de las necesarias

La velocidad de ejecución de informes (y selectores) se basa en la cantidad total de filas que mostraría el informe, sin importar si las iteras. Esto significa que siempre debes usar filtros específicos para minimizar el conjunto de resultados tanto como sea posible para que coincida con tu caso de uso.

Por ejemplo, supongamos que deseas encontrar grupos de anuncios con ofertas que se encuentran fuera de un rango específico. Sería más rápido realizar dos consultas distintas, una para ofertas por debajo del umbral inferior y otra para ofertas por encima del límite superior, en lugar de recuperar todos los grupos de anuncios e ignorar los que no te interesan.

Enfoque de programación Fragmento de código
Usar dos consultas (recomendado)
var adGroups = []
var report = AdsApp.search(
    'SELECT ad_group.name, ad_group.cpc_bid_micros' +
    ' FROM ad_group WHERE ad_group.cpc_bid_micros < 1000000');

while (report.hasNext()) {
  var row = report.next();
  adGroups.push(row.adGroup);
}
var report = AdsApp.search(
    'SELECT ad_group.name, ad_group.cpc_bid_micros' +
    ' FROM ad_group WHERE ad_group.cpc_bid_micros > 2000000');

while (report.hasNext()) {
  var row = report.next();
  adGroups.push(row.adGroup);
}
Cómo filtrar desde una consulta genérica (no recomendado)
var adGroups = []
var report = AdsApp.search(
    'SELECT ad_group.name, ad_group.cpc_bid_micros' +
    ' FROM ad_group');

while (report.hasNext()) {
  var row = report.next();
  var cpcBidMicros = row.adGroup.cpcBidMicros;
  if (cpcBidMicros < 1000000 || cpcBidMicros > 2000000) {
    adGroups.push(row.adGroup);
  }
}

Secuencias de comandos del Administrador de anuncios (MCC)

Prioriza ejecutar runInParallel en vez de la ejecución en serie

Cuando escribas secuencias de comandos para cuentas de administrador, usa executeInParallel() en lugar de la ejecución en serie cuando sea posible. executeInParallel() le da a la secuencia de comandos más tiempo de procesamiento (hasta una hora) y hasta 30 minutos por cuenta procesada (en lugar de 30 minutos combinados para la ejecución en serie). Consulta la página de límites para obtener más detalles.

Hojas de cálculo

Usar operaciones por lotes cuando actualices hojas de cálculo

Cuando actualices hojas de cálculo, intenta usar los métodos de operaciones masivas (por ejemplo, getRange()) en lugar de métodos que actualicen una celda a la vez.

Considera el siguiente fragmento de código que genera un patrón fractal en una hoja de cálculo.

Enfoque de programación Fragmento de código
Actualiza un rango de celdas en una sola llamada (recomendado)
var colors = new Array(100);
for (var y = 0; y < 100; y++) {
  xcoord = xmin;
  colors[y] = new Array(100);
  for (var x = 0; x < 100; x++) {
    colors[y][x] = getColor_(xcoord, ycoord);
    xcoord += xincrement;
  }
  ycoord -= yincrement;
}
sheet.getRange(1, 1, 100, 100).setBackgroundColors(colors);
Actualizar una celda a la vez (no recomendado)
var cell = sheet.getRange('a1');
for (var y = 0; y < 100; y++) {
  xcoord = xmin;
  for (var x = 0; x < 100; x++) {
    var c = getColor_(xcoord, ycoord);
    cell.offset(y, x).setBackgroundColor(c);
    xcoord += xincrement;
  }
  ycoord -= yincrement;
  SpreadsheetApp.flush();
}

Si bien Hojas de cálculo de Google intenta optimizar el segundo fragmento de código almacenando valores en caché, aún brinda un rendimiento deficiente en comparación con el primer fragmento, debido a la cantidad de llamadas a la API que se realizan.