> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK: analytics, DNS and domains

> Read edge analytics, errors, request logs and performance of a web application, check DNS, set a custom domain and purge the cache with c.Apps.Network.

`c.Apps.Network` works on **web apps only**. Every method takes `ctx` and the app id first.

Each snippet below runs on its own inside this program: paste one at a time into `main` and run `goimports` (see [Running the examples](/en/sdks/go/client#running-the-examples)).

```go theme={"system"}
package main

import (
	"context"
	"os"
	"time"

	"github.com/squarecloudofc/sdk-api-go/v3"
)

var (
	ctx   = context.Background()
	c     = squarecloud.New(os.Getenv("SQUARECLOUD_API_KEY"))
	appID = "abc123def456abc123def456"
	end   = time.Now()
	start = end.Add(-24 * time.Hour) // the last 24 hours
)

func main() {
	// Paste one snippet here, then run goimports.
}
```

## Time windows

`Analytics`, `Errors`, `Logs` and `Performance` take a `start` and an `end`, each a `time.Time` (sent as RFC 3339 in UTC). The program above sets them to the last 24 hours. A window is at most **7 days**, and `start` is clamped to the app's creation date. An invalid window is 400 `INVALID_TIME_RANGE`.

Results are cached in 5-minute buckets. Cache misses of these four methods share a limit of 20 requests per 60 seconds per account: going over it is 429 `RATE_LIMITED`.

## Analytics

`Network.Analytics(ctx, appID, start, end, filters)` returns traffic totals and breakdowns as a `*NetworkAnalytics`. It returns `nil` (and a `nil` error) for a window with no traffic. Pass an empty `squarecloud.AnalyticsFilters{}` for no filters.

```go theme={"system"}
analytics, err := c.Apps.Network.Analytics(ctx, appID, start, end, squarecloud.AnalyticsFilters{})
if err != nil {
	log.Fatal(err)
}

if analytics != nil {
	fmt.Println(analytics.Visits)    // []AnalyticsTimeBucket: Date, Visits, Requests, Bytes
	fmt.Println(analytics.Countries) // []AnalyticsBucket: Type ("BR"), Visits, Requests, Bytes
}
```

The breakdowns are `Countries`, `Devices`, `OS`, `Browsers`, `Protocols`, `Methods`, `Paths`, `Referers`, `Providers`, `IPs`, `StatusCodes`, `Bots` and `ContentTypes` (JSON `countries`, ..., `status_codes`, `bots`, `content_types`). Each bucket has `Type`, `Visits`, `Requests` and `Bytes` (`int64`).

### Filters

Filters narrow every breakdown. Each takes the exact `Type` value of its breakdown. An empty field is not sent:

```go theme={"system"}
brazil, err := c.Apps.Network.Analytics(ctx, appID, start, end, squarecloud.AnalyticsFilters{
	Country:  "BR",
	Path:     "/api",
	Provider: "GOOGLE (15169)",
})
if err != nil {
	log.Fatal(err)
}

if brazil != nil {
	fmt.Println(brazil.Paths)
}
```

| Filter | Example |
| - | - |
| `Country` | `"BR"` (2-letter code) |
| `IP` | `"203.0.113.7"` |
| `Path` | `"/api"` (path prefix) |
| `Status` | `"404"` |
| `OS`, `Browser`, `Protocol`, `Bot` | The breakdown's `Type` |
| `Referer` | `"Direct"` means no referer |
| `Provider` | `"NAME (ASN)"`, e.g. `"GOOGLE (15169)"` |
| `ContentType` | The breakdown's `Type` |

An invalid filter is 400 `INVALID_FILTER`.

## Errors

`Network.Errors(ctx, appID, start, end, include4xx)` returns the 5xx responses of the window, plus 4xx when `include4xx` is `true`. It returns `nil` for an empty window.

```go theme={"system"}
errs, err := c.Apps.Network.Errors(ctx, appID, start, end, true)
if err != nil {
	log.Fatal(err)
}

if errs != nil {
	fmt.Println(errs.Summary.Total, errs.Summary.ByClass) // ByClass: map[string]int64 with "4xx", "5xx"
	fmt.Println(errs.TopPaths)
}
```

## Request logs

`Network.Logs(ctx, appID, start, end)` returns individual requests with client, request and response details. Available on Pro and Enterprise plans (403 `UPGRADE_REQUIRED` otherwise).

```go theme={"system"}
logs, err := c.Apps.Network.Logs(ctx, appID, start, end)
if err != nil {
	log.Fatal(err)
}
for _, l := range logs {
	fmt.Println(l.Timestamp, l.Request.Method, l.Request.Path, l.Response.Status)
}
```

Fields the API may send as `null` are pointers (`Client.IP`, `Client.Country`, `Request.Query`, `Request.Referer`, `Response.Cache`, ...).

## Performance

`Network.Performance(ctx, appID, start, end)` returns latency percentiles (`P50`, `P95`, `P99`, in ms) at the edge and at the origin, by country, by location and for the slowest paths. It returns `nil` for an empty window. Available on Pro and Enterprise plans.

Each percentile is a `*float64`, `nil` when there were no requests to measure:

```go theme={"system"}
performance, err := c.Apps.Network.Performance(ctx, appID, start, end)
if err != nil {
	log.Fatal(err)
}

if performance != nil {
	if p95 := performance.Summary.Edge.P95; p95 != nil {
		fmt.Println("edge p95", *p95)
	}
	if p95 := performance.Summary.Origin.P95; p95 != nil {
		fmt.Println("origin p95", *p95)
	}
}
```

## DNS

`Network.DNS(ctx, appID)` returns the DNS records your custom domain needs, with their status. Without a custom domain it is 400 `NO_CUSTOM_DOMAIN`.

```go theme={"system"}
records, err := c.Apps.Network.DNS(ctx, appID)
if err != nil {
	log.Fatal(err)
}
for _, r := range records {
	fmt.Println(r.Type, r.Name, r.Value, r.Status) // Type: "txt" | "cname"
}
```

## Custom domain

`Network.SetDomain(ctx, appID, domain)` attaches a custom domain (Standard plan and up). Pass `"@"` to remove it.

```go theme={"system"}
if err := c.Apps.Network.SetDomain(ctx, appID, "www.example.com"); err != nil {
	log.Fatal(err)
}

// Remove it
if err := c.Apps.Network.SetDomain(ctx, appID, "@"); err != nil {
	log.Fatal(err)
}
```

| Status | Code | When |
| - | - | - |
| 400 | `INVALID_DOMAIN` | Not a valid domain name |
| 400 | `RESERVED_DOMAIN` | A Square Cloud domain |
| 403 | `UPGRADE_REQUIRED` | The plan has no custom domains |
| 403 | `LOAD_BALANCER_LIMIT_REACHED` | The plan's limit of apps on this domain was reached |
| 409 | `DOMAIN_ALREADY_EXISTS` | The domain belongs to an app of another account |
| 502 | `DNS_FAILED` | The edge provider refused the domain |

## Purging the cache

`Network.PurgeCache(ctx, appID)` clears the app's edge cache. It is limited to one purge every 60 seconds (429 `KEEP_CALM`).

```go theme={"system"}
if err := c.Apps.Network.PurgeCache(ctx, appID); err != nil {
	log.Fatal(err)
}
```

## Errors of the analytics methods

| Status | Code | When |
| - | - | - |
| 400 | `INVALID_TIME_RANGE` | `start`/`end` missing, malformed or inverted |
| 400 | `INVALID_FILTER` | A filter does not match its format (`Analytics`) |
| 403 | `UPGRADE_REQUIRED` | `Logs` and `Performance` outside Pro and Enterprise |
| 429 | `RATE_LIMITED` | Shared limit of cache misses reached |
| 500 | `UNABLE_TO_FETCH_ANALYTICS`, `UNABLE_TO_FETCH_ERRORS`, `UNABLE_TO_FETCH_PERFORMANCE` | The edge provider failed |
| 503 | `ANALYTICS_BUSY` | Edge analytics are busy: retried automatically |

## Next steps

<CardGroup cols={3}>
  <Card title="Realtime" icon="wave-pulse" href="/en/sdks/go/realtime">
    Stream live logs and status.
  </Card>

  <Card title="Network API reference" icon="code" href="/en/api-reference/endpoint/apps/network/analytics">
    The REST endpoints behind these methods.
  </Card>

  <Card title="Edge analytics from the CLI" icon="terminal" href="/en/cli-reference/edge-analytics">
    The same actions from the terminal.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.