Skip to content
ceaksan

Data Studio Community Connector: Niche Source Integration with Apps Script

Data Studio Community Connector architecture, the four required Apps Script functions, Head Deployment test flow, versioned production deployment, and the real cost of writing your own connector instead of paying $90/month for Supermetrics.

May 24, 2026 9 min read
TL;DR

A Community Connector is an Apps Script add-on that ships four required functions (getAuthType, getConfig, getSchema, getData) and acts as the official extension point for connecting any data source to Data Studio. It looks like a free alternative to $90/month Supermetrics subscriptions, but it is not free of cost: maintenance, error handling, and adapting to upstream API changes are entirely on the developer. This article walks through the four-function skeleton, the Head Deployment test flow, versioned production deployment, five production failure modes, and how to put cost guardrails around BigQueryConfig.


If the source you want to connect to Data Studio isn’t covered by the standard Google connectors (GA4, Search Console, Ads, Sheets, BigQuery), you have two options: pay for a bridge like Supermetrics, or write your own Community Connector. This article maps out the second path, the four required Apps Script functions, the versioned deployment flow, and the maintenance cost most agencies still underestimate.

Context: on April 10, 2026, Looker Studio officially reverted to the Data Studio name1. The separation from Looker (the enterprise BI product) is now explicit, and the Community Connector development surface remained unchanged through every rename. The Apps Script Data Studio service2 is still the official development interface.

What a Community Connector Is and Isn’t

A Community Connector is an Apps Script add-on that bridges any internet-reachable data source to Data Studio. The official definition: “scripts to access and modify Data Studio Community Connectors”2. In practice it has three layers:

  1. Apps Script code — four required functions and helper utilities
  2. Manifest (appsscript.json) — metadata so Data Studio can recognize the connector
  3. Deployment — Head (development) and versioned (production) releases

Connectors are designed for APIs or niche sources Data Studio doesn’t know about. Common use cases:

  • E-commerce platform reports (custom Shopify, WooCommerce reports)
  • Niche ad networks (anything outside the big three: TikTok, Reddit Ads, Quora Ads)
  • Custom CRM or HRIS systems
  • Public open datasets
  • Internal REST APIs

A connector is not an ETL engine (transformation belongs at the source), not a real-time stream (it’s bound to cache and refresh intervals), and not a dashboard (only a data provider).

When to Write a Community Connector

The decision turns on three properties of the target source:

DimensionCustom connector makes sensePaid solution (Supermetrics, Funnel.io) makes sense
Source count1-3 niche APIs5+ different platforms
API stabilityStable, versionedFrequent breaking changes
Maintenance time2-3 hours/month availableMaintenance must leave the in-house team
BudgetZero direct cost, dev time available$90-300/month subscription is fine
PrivacyData must land in your own GCPThird-party cloud is OK

A line from Whatagraph’s 2026 review puts it bluntly:

“Community connectors are great in theory. Sketchy in production. If the dev who built it doesn’t maintain it, you’re on your own.”

That sentence is the one-paragraph summary you should keep in mind before committing to a custom connector. The sketchy in production warning reflects the maintenance asymmetry between the 2% of officially Google-built connectors and the 98% community-built ones: the gallery lists 24 stable Google-built connectors versus 1,188 third-party connectors.

The Four Required Functions

Every Community Connector must export four global functions. If any of them is missing, Data Studio refuses to load the connector.

getAuthType()

A minimal function that tells Data Studio which auth mechanism the connector uses.

var cc = DataStudioApp.createCommunityConnector();

function getAuthType() {
  var AuthTypes = cc.AuthType;
  return cc
    .newAuthTypeResponse()
    .setAuthType(AuthTypes.KEY)
    .setHelpUrl("https://example.com/help")
    .build();
}

The Apps Script Data Studio service offers seven auth types2:

Auth typeUse case
NONEPublic API, no auth needed
OAUTH2Standard OAuth 2.0 (Sheets, GitHub, niche ad platforms)
USER_PASSBasic auth: username + password
PATH_USER_PASSAPI endpoint path + username + password
PATH_KEYAPI endpoint path + API key
KEYSingle API key or bearer token
USER_TOKENUsername + personal access token

Picking OAuth 2.0 forces extra functions: get3PAuthorizationUrls(), authCallback(), resetAuth(), isAuthValid(). OAuth 2.0 implementation is 4-5 times more code than a KEY-based auth, plus an explicit verification process.

getConfig(request)

Defines the configuration UI the user sees while adding the connector to Data Studio.

function getConfig() {
  var config = cc.getConfig();

  config
    .newInfo()
    .setId("instructions")
    .setText("Enter npm package names to fetch their download count.");

  config
    .newTextInput()
    .setId("package")
    .setName("Enter package name(s), comma-separated")
    .setHelpText('e.g. "googleapis" or "package,somepackage,anotherpackage"')
    .setPlaceholder("googleapis")
    .setAllowOverride(true);

  config.setDateRangeRequired(true);

  return config.build();
}

Configuration UI components: Info, TextInput, TextArea, Checkbox, SelectSingle, SelectMultiple. All builder-pattern.

For dynamic configuration (e.g. account → property → view chain), call config.setIsSteppedConfig(true). With stepped config enabled, getConfig() is re-invoked on every form change, so heavy API calls belong in getData(), not getConfig().

getSchema(request)

Defines the fields (dimensions and metrics) the connector will return.

function getFields() {
  var fields = cc.getFields();
  var types = cc.FieldType;
  var aggregations = cc.AggregationType;

  fields
    .newDimension()
    .setId("packageName")
    .setName("Package")
    .setType(types.TEXT);

  fields
    .newDimension()
    .setId("day")
    .setName("Date")
    .setType(types.YEAR_MONTH_DAY);

  fields
    .newMetric()
    .setId("downloads")
    .setName("Downloads")
    .setType(types.NUMBER)
    .setAggregation(aggregations.SUM);

  return fields;
}

function getSchema(request) {
  return { schema: getFields().build() };
}

The FieldType enum has 100+ values. The most common ones in practice:

  • Date: YEAR, YEAR_MONTH, YEAR_MONTH_DAY, YEAR_MONTH_DAY_HOUR, DAY_OF_WEEK, DURATION
  • Geo: COUNTRY, COUNTRY_CODE, REGION, CITY, LATITUDE_LONGITUDE
  • Currency: CURRENCY_USD, CURRENCY_EUR, CURRENCY_GBP, CURRENCY_TRY (90+ ISO codes)
  • Numeric: NUMBER, PERCENT, BOOLEAN
  • Text/media: TEXT, URL, HYPERLINK, IMAGE, IMAGE_LINK

A practical note for international e-commerce: ISO currency codes are first-class, so you don’t need extra calculated fields to format local currency in Data Studio.

getData(request)

The function where the actual data fetch happens. The request object carries three critical parameters:

  • request.fields — the fields Data Studio is asking for (not always the full schema)
  • request.configParams — user input from getConfig()
  • request.dateRange — if setDateRangeRequired(true), { startDate, endDate }
  • request.lastRefresh — last refresh timestamp, critical for cache decisions
function getData(request) {
  request.configParams = validateConfig(request.configParams);

  var requestedFields = getFields().forIds(
    request.fields.map(function (field) {
      return field.name;
    }),
  );

  try {
    var apiResponse = fetchDataFromApi(request);
    var normalizedResponse = normalizeResponse(request, apiResponse);
    var data = getFormattedData(normalizedResponse, requestedFields);
  } catch (e) {
    cc.newUserError()
      .setDebugText("Error fetching data from API. Exception details: " + e)
      .setText(
        "The connector has encountered an unrecoverable error. Please try again later, or file an issue if this error persists.",
      )
      .throwException();
  }

  return {
    schema: requestedFields.build(),
    rows: data,
  };
}

function fetchDataFromApi(request) {
  var url = [
    "https://api.npmjs.org/downloads/range/",
    request.dateRange.startDate,
    ":",
    request.dateRange.endDate,
    "/",
    request.configParams.package,
  ].join("");
  return UrlFetchApp.fetch(url);
}

getData() is the heart of the performance and cost layer. Three practical rules:

  1. Process only requested fields. Without the requestedFields = getFields().forIds(...) filter, you’ll do extra computation and transfer on fields Data Studio never asked for.
  2. Cache against lastRefresh. Apps Script Cache Service3 lets you serve cached data when not enough time has passed since the last fetch.
  3. try/catch + UserError pattern. Uncaught exceptions produce a generic “Internal error” in the chart. User-facing messages must be expressed through UserError.

Manifest

Open appsscript.json from Apps Script editor → View → Show manifest file. The Data Studio block is required:

{
  "dependencies": {
    "libraries": []
  },
  "dataStudio": {
    "name": "npm Downloads",
    "logoUrl": "https://raw.githubusercontent.com/npm/logos/master/npm%20square/n-64.png",
    "company": "Your Company",
    "companyUrl": "https://example.com",
    "addonUrl": "https://example.com/connector",
    "supportUrl": "https://example.com/support",
    "description": "Get npm package download counts.",
    "sources": ["npm"]
  },
  "oauthScopes": ["https://www.googleapis.com/auth/script.external_request"]
}

The oauthScopes list is zero-tolerance: every Apps Script resource your connector touches must be declared explicitly. For external HTTP requests, script.external_request; Cache Service has no separate scope (it falls under script.scriptapp implicitly). Missing scopes surface at runtime, not at publish time.

Testing with Head Deployment

Apps Script automatically updates the Head Deployment on every save. This deployment always runs the latest code; the development cycle is much faster than production releases.

Test flow:

  1. Apps Script editor → Deploy → Test deployments → copy the Head Deployment ID
  2. Open in browser:
    https://datastudio.google.com/datasources/create?connectorId=HEAD_DEPLOYMENT_ID
  3. Data Studio loads the connector and invokes getAuthType()getConfig()getSchema()getData() in sequence
  4. After each save, refresh the browser; the new code takes effect immediately

UrlFetchApp.fetch calls are visible in Apps Script execution logs (View → Executions). For debugging, prefer console.log() over Logger.log(); the output is more readable.

Versioned Production Deployment

Because the Head Deployment changes on every save, it should never be exposed to end users. For production, keep two separate versioned deployments:

DeploymentPurpose
HeadDevelopment, auto-updates on every save
TestStable test version, manually pinned
ProductionEnd users, manually pinned

To ship a new version:

  1. Apps Script → Deploy → New deployment
  2. Type: Add-on (this is what Community Connectors use)
  3. Pin the version (auto-numbered)
  4. Copy the deployment ID + connector URL
  5. Share the URL with end users or internal teams

To update an existing deployment: Deploy → Manage deployments → Edit → pick the new version. End users do not need a re-share; the same deployment URL automatically serves the new version.

Error Handling: UserError and DebugError

The Apps Script Data Studio service offers two error object types2:

// End user sees this
cc.newUserError()
  .setText("Invalid API key. Please check your credentials.")
  .setDebugText("API returned 401 with body: " + responseBody)
  .throwException();

// Only the developer / admin sees this
cc.newDebugError()
  .setText("Schema mismatch: expected 5 columns, got 7")
  .throwException();

The distinction matters: the user-friendly text and the technical debug text are different fields on the same object. DebugError is never shown to end users; it only appears in Apps Script execution logs. Messages with PII or that expose system architecture belong in UserError.debugText, not UserError.text.

Workspace Admin Context

If you’re distributing the connector inside a Google Workspace organization, validate two policy layers:

  1. Is Apps Script enabled? Admin console → Apps → Google Workspace → Drive and Docs → Google Apps Script. If disabled, the connector cannot fetch data and users see: Can't show data. Apps Script is disabled for your organization.

  2. Third-party connector policy? Workspace admin → Data Studio settings → Community connector data source creation. If disabled: Community connectors are disabled for your organization.

These two policies are independent. Apps Script can be enabled while the Data Studio policy is denied. Confirming policy state with the admin before deployment cuts down “doesn’t work for users” support tickets afterwards.

Edge case where Apps Script is ON but Drive and Docs is OFF: the user cannot create or edit scripts, but existing scripts continue to run. A connector deployed in a Drive-disabled environment works normally for end users, only code editing is blocked.

Production Failure Modes

Five failure modes show up consistently after a custom connector is deployed:

ErrorCauseFix
Connector internal errorgetData() failed to parse API responsetry/catch + UserError, fallback data row
Service invoked too many times in a short time: exec qpsApps Script execution quotaCache Service + batched UrlFetchApp calls
”Apps Script is disabled for your organization”Workspace admin disabled Apps ScriptExplicit handoff to the admin
Real-time refresh failsAPI quota or cache missAdd an Extract Data connector as a cache layer
Auth fails after working initiallyToken expired or scope changedReauthorize data source; if needed, resetAuth()

Cache Service usage is critical. For free Workspace accounts, UrlFetchApp has a daily limit of 20,000 calls; premium accounts go above 100,000. As connector usage grows, this limit fills faster than expected. Cache pattern:

var cache = CacheService.getScriptCache();
var cacheKey =
  "data_" + request.configParams.package + "_" + request.dateRange.startDate;
var cached = cache.get(cacheKey);

if (cached) {
  return JSON.parse(cached);
}

var freshData = fetchDataFromApi(request);
cache.put(cacheKey, JSON.stringify(freshData), 1800); // 30 minutes
return freshData;

Cache TTL should be slightly shorter than Data Studio’s refresh interval (e.g., Data Studio refreshes every hour → cache TTL 50-55 minutes). Equal or longer TTL increases the risk of stale data.

BigQueryConfig: Running SQL from Inside the Connector

If the target data already lives in BigQuery, use the BigQueryConfig builder2. This makes Apps Script call the native BigQuery API instead of UrlFetchApp.

var config = cc
  .newBigQueryConfig()
  .setAccessToken(token)
  .setBillingProjectId("my-project")
  .setUseStandardSql(true)
  .setQuery("SELECT * FROM dataset.table WHERE date = @date")
  .addQueryParameter("date", cc.BigQueryParameterType.STRING, "2026-05-24");

Cost containment is three-layered:

  1. Query-level limit. Set Maximum bytes billed on the BigQuery query (More → Query settings → Advanced); exceeding the limit fails the query at zero cost. Wire this limit into the connector’s SQL wrapper.

  2. Schema-level discipline. Tables created with PARTITION BY date_col CLUSTER BY high_cardinality_col + OPTIONS (require_partition_filter=true) block unfiltered queries by design. A connector user can’t accidentally trigger a full table scan.

  3. Zero-cost operation discipline. SELECT DISTINCT day FROM table scans 149 GB / $1.18 in one benchmark, while the same answer through INFORMATION_SCHEMA.PARTITIONS scans 10 MB / $0.00008 — a 14,900x gap4. Prefer INFORMATION_SCHEMA for helper queries inside the connector.

LIMIT 1000 doesn’t reduce query cost; BigQuery scans the whole table and only the row count returned drops. To actually reduce cost, use TABLESAMPLE SYSTEM (X PERCENT) or a column subset (specific columns instead of SELECT *)4.

Maintenance: The Silent Cost

Writing a custom connector typically takes 8-16 hours. That’s not the real cost; the real cost is the post-deploy maintenance. Typical annual maintenance items:

  • Adapting to upstream API breaking changes — V1 to V2 migrations, response shape changes
  • OAuth scope updates — Google or third-party scope policy changes force re-auth
  • Rate-limit recalibration — as users grow, the cache strategy needs revision
  • Schema changes — when users request new fields, both getSchema() and getData() evolve in lockstep
  • Workspace admin policy changes — if a user organization revises Apps Script policy, support tickets follow

The Whatagraph practitioner note again:

“You can build your own or use open-source ones published by others. This adds flexibility, but it’s not plug-and-play. You’ll need technical expertise, API familiarity, and time to maintain it.”

Before committing to a custom connector, mentally sign a 24-month maintenance contract. Otherwise the connector ships, the upstream API breaks six months later, nobody fixes it, and users start silently reporting wrong data. Silent data truncation is one of the most common production failure modes observed with Data Studio5.

Distribution Options

Two distribution models after deployment:

  1. Direct share. Send the deployment URL to the target user, and they create a data source via the connectorId=... URL. Sufficient for internal teams and agency-client setups.

  2. Public Gallery submission. Submit to Google’s official Connector Gallery6. The review takes weeks. Once approved, the connector becomes searchable for all Data Studio users. For marketing-analytics niches, a public connector can be a brand-exposure and lead-generation channel; but approval requires solid docs and support capacity.

The direct-share model covers 90% of agency-client and internal-org scenarios. Public gallery without production-grade documentation and support hurts more than it helps; the Data Studio gallery already has abandoned-connector entries.

Decision Summary

QuestionAnswer
Is the target source niche and stable?Custom connector makes sense
Do you need 5+ different sources?Supermetrics or Funnel.io makes sense
Can you commit 2-3 hours/month for maintenance?Custom connector makes sense
Does data privacy block third-party clouds?Custom connector makes sense
Is Apps Script time unavailable for the developer?Paid solution
Is the data already in BigQuery?BigQueryConfig or the native BQ connector

The next post in this cluster, When Is Data Studio Pro Worth It?, covers Pro tier’s contribution to Community Connectors and again the maintenance economics; since Pro is a flat per-user monthly subscription, the decision hangs less on customer count and more on saved manual workload.

Let's Talk Through Your Connector Strategy

Niche API integration, OAuth scope design, Apps Script execution quota planning, and BigQuery cost guardrails. Answering the custom-vs-paid question for your specific scenario.

Get in Touch

Footnotes

  1. Looker Studio is Data Studio (Google Cloud Blog, April 10, 2026)
  2. Apps Script Data Studio Service Reference 2 3 4 5
  3. Apps Script Cache Service
  4. Volker Janz, “Burn Data Rather Than Money with BigQuery: The Definitive Guide” (2024) 2
  5. r/BusinessIntelligence — “Looker Studio vs Others” practitioner thread (2024)
  6. Data Studio Connector Gallery
Key Takeaways
  • 01 A Community Connector is an Apps Script add-on with four required functions: getAuthType, getConfig, getSchema, getData
  • 02 Use the Head Deployment URL for testing and keep versioned deployments (Test and Production) separate
  • 03 Pick the auth type (OAuth 2.0, KEY, USER_PASS, PATH_KEY) based on the upstream API's authentication protocol
  • 04 For Workspace audiences, validate Apps Script and Data Studio admin policies before shipping the connector
  • 05 Custom connectors look free but maintenance, error handling, and adapting to breaking API changes are entirely on the developer
  • 06 With BigQueryConfig, the trio of Maximum bytes billed, cluster filters, and Cache Service is the foundation of cost control
Frequently Asked Questions (FAQ)
+ Do I need to know Apps Script to write a Community Connector?

Yes, Community Connectors are officially developed on Apps Script. Standard JavaScript syntax is enough, V8 runtime is the default. The Apps Script Data Studio service (DataStudioApp.createCommunityConnector) provides all builders and enums, so no separate IDE setup is required.

+ Community Connector is not working on my Workspace account, why?

Workspace admin may have disabled Apps Script entirely, or under Data Studio settings the third-party connector data source creation policy may be denied. With Apps Script disabled, the connector cannot fetch data and charts return 'Can't show data. Apps Script is disabled for your organization'. The relevant policy must be opened per org-unit by the admin.

+ Custom Community Connector or paid solutions like Supermetrics?

If the target source is a niche, stable API, a custom connector is free to start, but the maintenance commitment shifts to the developer. Supermetrics ships 100+ ready-made connectors with support, SLAs, and breaking-change adaptation. If you only need one or two APIs and can spare two hours per month on maintenance, custom is sensible. Otherwise paid lowers operational cost.

+ Does Data Studio store user credentials?

No. Data Studio does not store the credentials a user gives a connector (API key, OAuth token, username/password). On every fetch, the connector passes the credential to the third-party API and asks 'is this still valid?'. While this is by design for security, it forces the connector developer to write code resilient to token rotation and scope changes.

+ How do I cap costs if the connector runs SQL against BigQuery?

If you use the BigQueryConfig builder, the connector runs queries directly on the user's BigQuery project. Cost control requires two layers: a query-level Maximum bytes billed limit and dataset-level partition + cluster discipline (require_partition_filter=true). Inside the connector code, comparing lastRefresh with Apps Script Cache Service is critical to avoid unnecessary BigQuery queries.