Documentation
¶
Overview ¶
Package coworkingview is the Go client for the CoworkingView catalogue API: coworking spaces, private offices, flexible desks and meeting rooms, with published prices and market rates by city.
CoworkingView compares flexible workspace across cities such as Berlin, Madrid, Barcelona and London. This package reads the same catalogue over its public /v1 API at https://api.coworkingview.com.
Reads are free and need no API key, no sign-up and no sales call. The API is rate-limited; production is the sandbox.
Quick start ¶
client := coworkingview.NewClient()
page, err := client.ListProperties(ctx, &coworkingview.ListPropertiesParams{
City: "berlin",
PricingOptionType: coworkingview.PrivateOffice,
PageSize: 5,
})
if err != nil {
log.Fatal(err)
}
for _, p := range page.Data {
fmt.Println(p.Title, p.FromPrice)
}
What the API answers ¶
- Spaces: Client.ListProperties, Client.AllProperties and Client.GetProperty (the full record, with every pricing option).
- Places and brands: Client.Bootstrap lists every country, city and district; Client.GetPlace and Client.GetOperator return their profiles.
- Market data: Client.MarketRates returns the medians behind the Coworking Price Index, per city and workspace type.
- Search: Client.Suggest autocompletes cities, districts, brands and spaces.
Reading the numbers ¶
Market rates are medians over the priced spaces in a city. Every figure carries a SampleSize: read it before calling a figure "the market rate". Prices are what operators publish; Property.WebURL links to the live listing, and the Review Index ranks the same cities by their public reviews.
Other ways in ¶
The same catalogue is available as an MCP server for AI assistants, a CLI and an OpenAPI 3.1 document: see CoworkingView for developers and the OpenAPI specification.
Index ¶
- Constants
- func IsNotFound(err error) bool
- type APIError
- type BillingPeriod
- type Bootstrap
- type BuildingPrice
- type Citation
- type City
- type CityRates
- type Client
- func (c *Client) AllProperties(ctx context.Context, params *ListPropertiesParams) iter.Seq2[PropertySummary, error]
- func (c *Client) Bootstrap(ctx context.Context, locale Locale) (*Response[Bootstrap], error)
- func (c *Client) Facets(ctx context.Context, params *ListPropertiesParams) (*Response[Facets], error)
- func (c *Client) GetOperator(ctx context.Context, slug string, locale Locale) (*Response[Operator], error)
- func (c *Client) GetPlace(ctx context.Context, kind PlaceKind, slug string, locale Locale) (*Response[Place], error)
- func (c *Client) GetProperty(ctx context.Context, slug string, locale Locale) (*Response[Property], error)
- func (c *Client) Health(ctx context.Context) (*Response[HealthStatus], error)
- func (c *Client) ListProperties(ctx context.Context, params *ListPropertiesParams) (*Response[[]PropertySummary], error)
- func (c *Client) MarketRates(ctx context.Context, params *MarketRatesParams) (*Response[MarketRates], error)
- func (c *Client) Suggest(ctx context.Context, q string, limit int, locale Locale) (*Response[[]Suggestion], error)
- type Coordinates
- type Country
- type FAQ
- type Facet
- type FacetValue
- type Facets
- type FieldError
- type HealthStatus
- type Image
- type ListPropertiesParams
- type Locale
- type MarketPosition
- type MarketRates
- type MarketRatesParams
- type Meta
- type Money
- type Operator
- type OperatorRef
- type Option
- type Pagination
- type Place
- type PlaceKind
- type PlaceRef
- type PricingOption
- type PricingOptionType
- type Property
- type PropertySummary
- type RateByType
- type RateFigure
- type RateStat
- type Response
- type SortBy
- type Suggestion
- type UnpublishedCity
- type Verdict
- type Zone
Examples ¶
Constants ¶
const DefaultBaseURL = "https://api.coworkingview.com"
DefaultBaseURL is the origin of the public CoworkingView API.
const DefaultTimeout = 15 * time.Second
DefaultTimeout bounds every request made by a client built without WithHTTPClient. An unreachable API must never wedge a caller.
const Version = "0.1.0"
Version is this client's version, sent in the User-Agent header.
Variables ¶
This section is empty.
Functions ¶
func IsNotFound ¶
IsNotFound reports whether err is the API saying the slug does not exist (404) or no longer exists (410, e.g. a space that closed).
Types ¶
type APIError ¶
type APIError struct {
StatusCode int `json:"status"`
Type string `json:"type"`
Title string `json:"title"`
Detail string `json:"detail,omitempty"`
Code string `json:"code,omitempty"`
Errors []FieldError `json:"errors,omitempty"`
}
APIError is a non-2xx answer from the API. The API speaks RFC 9457 problem+json, so Title and Detail are human-readable and Code is stable.
type BillingPeriod ¶
type BillingPeriod string
BillingPeriod is what a price covers.
const ( PerHour BillingPeriod = "hour" PerDay BillingPeriod = "day" PerWeek BillingPeriod = "week" PerMonth BillingPeriod = "month" PerYear BillingPeriod = "year" Once BillingPeriod = "once" )
Billing periods.
type Bootstrap ¶
type Bootstrap struct {
Countries []Country `json:"countries"`
Cities []City `json:"cities"`
// Currencies maps an ISO 3166-1 country code to its local currency.
Currencies map[string]string `json:"currencies"`
}
Bootstrap is every country, city and district in the catalogue, with how many spaces each holds. Use it to turn a name into the slug the other calls take.
type BuildingPrice ¶
type BuildingPrice struct {
Price Money `json:"price"`
Name string `json:"name,omitempty"`
Slug string `json:"slug,omitempty"`
OperatorName string `json:"operatorName,omitempty"`
OperatorSlug string `json:"operatorSlug,omitempty"`
}
BuildingPrice is one space's entry price in CityRates.Buildings.
type Citation ¶
type Citation struct {
Source string `json:"source"`
Title string `json:"title"`
URL string `json:"url"`
Accessed string `json:"accessed"`
Text string `json:"text"`
}
Citation is how to credit CoworkingView when you republish a figure.
type City ¶
type City struct {
Slug string `json:"slug"`
Name string `json:"name"`
CountryCode string `json:"countryCode"`
PropertyCount int `json:"propertyCount"`
Center Coordinates `json:"center"`
Icon *Image `json:"icon,omitempty"`
Zones []Zone `json:"zones"`
}
City is one city in Bootstrap, with its districts.
type CityRates ¶
type CityRates struct {
City PlaceRef `json:"city"`
CountryCode string `json:"countryCode,omitempty"`
Currency string `json:"currency"`
// Figures are the published medians per workspace type. Prefer them over
// ByType, which stays for older clients and is empty when a figure would
// have required a min or max.
Figures []RateFigure `json:"figures,omitempty"`
ByType []RateByType `json:"byType"`
// DayPass and MeetingRoom are headline medians for the two pay-as-you-go products.
DayPass *RateStat `json:"dayPass,omitempty"`
MeetingRoom *RateStat `json:"meetingRoom,omitempty"`
PropertyCount int `json:"propertyCount,omitempty"`
OperatorCount int `json:"operatorCount,omitempty"`
InsufficientData bool `json:"insufficientData,omitempty"`
// Buildings lists each space's entry price, with its operator.
Buildings []BuildingPrice `json:"buildings,omitempty"`
// EntryMedian, EntryMin and EntryMax describe each space's entry price.
EntryMedian *Money `json:"entryMedian,omitempty"`
EntryMin *Money `json:"entryMin,omitempty"`
EntryMax *Money `json:"entryMax,omitempty"`
Min *Money `json:"min,omitempty"`
Max *Money `json:"max,omitempty"`
P25 *Money `json:"p25,omitempty"`
P75 *Money `json:"p75,omitempty"`
// SampleSize is the number of priced spaces in the city backing the
// aggregate, across all workspace types.
SampleSize int `json:"sampleSize"`
}
CityRates holds one city's market figures, the numbers behind its page on the Coworking Price Index.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client reads the CoworkingView catalogue. The zero value is not usable; build one with NewClient. A Client is safe for concurrent use.
func NewClient ¶
NewClient returns a client for the public API at DefaultBaseURL.
func (*Client) AllProperties ¶
func (c *Client) AllProperties(ctx context.Context, params *ListPropertiesParams) iter.Seq2[PropertySummary, error]
AllProperties walks every page of a list, yielding one space at a time. It stops at the first error, which it yields. Leave Page empty; PageSize sets how many spaces each request fetches.
for p, err := range client.AllProperties(ctx, &coworkingview.ListPropertiesParams{City: "madrid"}) {
if err != nil {
return err
}
fmt.Println(p.Title)
}
Example ¶
Walk every space in a city, across pages.
package main
import (
"context"
"fmt"
"log"
"github.com/coworkingview/coworkingview-go"
)
func main() {
client := coworkingview.NewClient()
count := 0
for _, err := range client.AllProperties(context.Background(), &coworkingview.ListPropertiesParams{City: "madrid"}) {
if err != nil {
log.Fatal(err)
}
count++
}
fmt.Println(count, "spaces in Madrid")
}
Output:
func (*Client) Facets ¶
func (c *Client) Facets(ctx context.Context, params *ListPropertiesParams) (*Response[Facets], error)
Facets returns the filters available for a scope (Locale, Country, City, Zone, Operator, PricingOptionType and Currency are read from params), with counts: the amenities, districts and brands you can filter a city by.
func (*Client) GetOperator ¶
func (c *Client) GetOperator(ctx context.Context, slug string, locale Locale) (*Response[Operator], error)
GetOperator returns a brand's profile, the data behind a brand page such as WeWork on CoworkingView.
func (*Client) GetPlace ¶
func (c *Client) GetPlace(ctx context.Context, kind PlaceKind, slug string, locale Locale) (*Response[Place], error)
GetPlace returns a place profile, the data behind a page such as coworking in Madrid.
func (*Client) GetProperty ¶
func (c *Client) GetProperty(ctx context.Context, slug string, locale Locale) (*Response[Property], error)
GetProperty returns one space by slug. A space that closed answers 410; test for both cases with IsNotFound.
Example ¶
Fetch one space and every product it sells.
package main
import (
"context"
"fmt"
"log"
"github.com/coworkingview/coworkingview-go"
)
func main() {
client := coworkingview.NewClient()
res, err := client.GetProperty(context.Background(), "signature-potsdamer-platz-berlin", coworkingview.English)
if coworkingview.IsNotFound(err) {
fmt.Println("that space is not listed")
return
}
if err != nil {
log.Fatal(err)
}
for _, o := range res.Data.PricingOptions {
if o.Price != nil {
fmt.Printf("%s: %s per %s\n", o.OptionType, o.Price, o.BillingPeriod)
}
}
fmt.Println("Details:", res.Data.WebURL)
}
Output:
func (*Client) ListProperties ¶
func (c *Client) ListProperties(ctx context.Context, params *ListPropertiesParams) (*Response[[]PropertySummary], error)
ListProperties returns one page of spaces matching params, the same list as a city page such as coworking in Berlin.
Example ¶
List private offices in Berlin with their entry price.
package main
import (
"context"
"fmt"
"log"
"github.com/coworkingview/coworkingview-go"
)
func main() {
client := coworkingview.NewClient()
res, err := client.ListProperties(context.Background(), &coworkingview.ListPropertiesParams{
City: "berlin",
PricingOptionType: coworkingview.PrivateOffice,
PageSize: 5,
})
if err != nil {
log.Fatal(err)
}
for _, p := range res.Data {
if p.FromPrice != nil {
fmt.Printf("%s from %s\n", p.Title, p.FromPrice)
}
}
}
Output:
func (*Client) MarketRates ¶
func (c *Client) MarketRates(ctx context.Context, params *MarketRatesParams) (*Response[MarketRates], error)
MarketRates returns what flexible workspace costs, per city and workspace type: medians, quartiles and the sample size behind each figure.
Example ¶
Read what a flex desk costs in Barcelona, with the sample behind the figure.
package main
import (
"context"
"fmt"
"log"
"github.com/coworkingview/coworkingview-go"
)
func main() {
client := coworkingview.NewClient()
res, err := client.MarketRates(context.Background(), &coworkingview.MarketRatesParams{City: "barcelona"})
if err != nil {
log.Fatal(err)
}
for _, city := range res.Data.Cities {
for _, f := range city.Figures {
if f.OptionType == coworkingview.FlexDesk {
fmt.Printf("%s flex desk median: %s/month (%d spaces)\n", city.City.Name, f.Median, f.PropertyCount)
}
}
}
}
Output:
type Coordinates ¶
Coordinates are WGS84 degrees.
type Country ¶
type Country struct {
Slug string `json:"slug"`
Name string `json:"name"`
Code string `json:"code"`
PropertyCount int `json:"propertyCount"`
}
Country is one country in Bootstrap.
type Facet ¶
type Facet struct {
Key string `json:"key"`
Label string `json:"label"`
Values []FacetValue `json:"values"`
}
Facet is one filter dimension with the values present in a result set.
type FacetValue ¶
type FacetValue struct {
Value string `json:"value"`
Label string `json:"label"`
Count int `json:"count"`
}
FacetValue is one filter value and how many spaces carry it.
type Facets ¶
type Facets struct {
Facets []Facet `json:"facets"`
PriceRange *struct {
Min Money `json:"min"`
Max Money `json:"max"`
} `json:"priceRange,omitempty"`
CapacityRange *struct {
Min int `json:"min"`
Max int `json:"max"`
} `json:"capacityRange,omitempty"`
}
Facets is the payload of Client.Facets.
type FieldError ¶
FieldError names one rejected query parameter in a validation failure.
type HealthStatus ¶
HealthStatus is the payload of Client.Health.
type Image ¶
type Image struct {
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
Alt string `json:"alt"`
ThumbHash string `json:"thumbHash,omitempty"`
}
Image is a photo or logo with its intrinsic size.
type ListPropertiesParams ¶
type ListPropertiesParams struct {
Locale Locale
Country string // country slug, e.g. "germany"
City string // city slug, e.g. "berlin"
Zone string // district slug, e.g. "mitte"
Operator string // brand slug, e.g. "wework"
PricingOptionType PricingOptionType
Amenities []string
MinCapacity int
MinPrice float64
MaxPrice float64
Currency string // ISO 4217; MinPrice/MaxPrice are in this currency
SortBy SortBy
// Near searches around a point; RadiusMeters (up to 200 km) bounds it and
// SortByDistance orders by it.
Near *Coordinates
RadiusMeters int
// Address narrows to listings at that street address, e.g. "Domstraße 17".
// It is not geocoded: to search around an address, use Near.
Address string
Page int // 1-based, up to 500
PageSize int // 1-50, default 20
// Seed keeps a SortRandom order stable across pages: reuse Meta.Seed from
// the first page. AllProperties does this for you.
Seed int
}
ListPropertiesParams filters Client.ListProperties. Every field is optional; slugs come from Client.Bootstrap or Client.Suggest.
type MarketPosition ¶
type MarketPosition struct {
Verdict Verdict `json:"verdict"`
Median Money `json:"median"`
SampleSize int `json:"sampleSize"`
}
MarketPosition compares a space's entry price with its city's median for the same kind of workspace.
type MarketRates ¶
type MarketRates struct {
Cities []CityRates `json:"cities"`
Unpublished []UnpublishedCity `json:"unpublished,omitempty"`
// TotalProperties and TotalPricePoints are the sample behind every figure.
TotalProperties int `json:"totalProperties"`
TotalPricePoints int `json:"totalPricePoints"`
AsOf string `json:"asOf"`
// License, LicenseURL and Attribution say how the figures may be reused
// (CC BY 4.0, with attribution and a link to the source).
License string `json:"license,omitempty"`
LicenseURL string `json:"licenseUrl,omitempty"`
Attribution string `json:"attribution,omitempty"`
Scope string `json:"scope,omitempty"`
Copyright string `json:"copyright,omitempty"`
}
MarketRates is the payload of Client.MarketRates: published city figures plus the cities held back for too small a sample.
type MarketRatesParams ¶
type MarketRatesParams struct {
Locale Locale
City string // city slug, e.g. "berlin"
Country string // country slug, e.g. "spain"
Currency string // ISO 4217; converts every figure. Default: each city's own currency.
}
MarketRatesParams scopes Client.MarketRates. Leave City and Country empty for every published city.
type Meta ¶
type Meta struct {
ContractVersion string `json:"contractVersion"`
Pagination *Pagination `json:"pagination,omitempty"`
Truncated bool `json:"truncated,omitempty"`
// Seed is the shuffle seed of a random ordering. Pass it back in
// ListPropertiesParams.Seed to page through the same order.
Seed *int `json:"seed,omitempty"`
Citation *Citation `json:"citation,omitempty"`
CatalogueNotice string `json:"catalogueNotice,omitempty"`
}
Meta travels with every response.
type Operator ¶
type Operator struct {
Slug string `json:"slug"`
Name string `json:"name"`
Logo *Image `json:"logo,omitempty"`
Tagline string `json:"tagline,omitempty"`
Description string `json:"description,omitempty"`
FAQs []FAQ `json:"faqs"`
PropertyCount int `json:"propertyCount"`
Cities []PlaceRef `json:"cities"`
// PricePosition places the brand's prices relative to each city's median.
PricePosition *struct {
Verdict Verdict `json:"verdict"`
SampleSize int `json:"sampleSize"`
} `json:"pricePosition,omitempty"`
AsOf string `json:"asOf"`
}
Operator is the profile of a coworking brand, such as WeWork or Regus.
type OperatorRef ¶
type OperatorRef struct {
Slug string `json:"slug"`
Name string `json:"name"`
Logo *Image `json:"logo,omitempty"`
}
OperatorRef is the brand that runs a space.
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithBaseURL ¶
WithBaseURL points the client at another origin, such as a local gateway.
func WithHTTPClient ¶
WithHTTPClient replaces the default *http.Client (and its timeout).
func WithLocale ¶
WithLocale sets the language used when a call's params leave Locale empty. The default is English.
func WithUserAgent ¶
WithUserAgent prefixes the User-Agent header, so the API's operators can tell your integration apart, e.g. "acme-office-finder/2.1".
type Pagination ¶
type Pagination struct {
Page int `json:"page"`
PageSize int `json:"pageSize"`
Total int `json:"total"`
PageCount int `json:"pageCount"`
}
Pagination describes one page of a list.
type Place ¶
type Place struct {
Kind PlaceKind `json:"kind"`
Slug string `json:"slug"`
Name string `json:"name"`
Intro string `json:"intro,omitempty"`
ShortDescription string `json:"shortDescription,omitempty"`
FAQs []FAQ `json:"faqs"`
PropertyCount int `json:"propertyCount"`
PriceRange *struct {
Min Money `json:"min"`
Max Money `json:"max"`
} `json:"priceRange,omitempty"`
Center *Coordinates `json:"center,omitempty"`
Parent *PlaceRef `json:"parent,omitempty"`
AsOf string `json:"asOf"`
}
Place is the profile of a country, city or district: its introduction, FAQs, how many spaces it has and their price range.
type PlaceKind ¶
type PlaceKind string
PlaceKind is the level of a place profile.
Place kinds accepted by Client.GetPlace.
type PricingOption ¶
type PricingOption struct {
OptionType PricingOptionType `json:"optionType"`
// Title is the operator's own name for the product, e.g. "Team office for 6".
Title string `json:"title"`
// Price is nil when PriceOnRequest is true: the operator does not publish it.
Price *Money `json:"price,omitempty"`
PriceOnRequest bool `json:"priceOnRequest"`
BillingPeriod BillingPeriod `json:"billingPeriod"`
MinCapacity int `json:"minCapacity,omitempty"`
MaxCapacity int `json:"maxCapacity,omitempty"`
}
PricingOption is one product a space sells, with its published price.
type PricingOptionType ¶
type PricingOptionType string
PricingOptionType is a kind of workspace a space sells.
const ( PrivateOffice PricingOptionType = "private_office" ExclusiveArea PricingOptionType = "exclusive_bereich" // a whole floor or wing for one team FixedDesk PricingOptionType = "fix_desk" FlexDesk PricingOptionType = "flex_desk" DayPass PricingOptionType = "day_pass" MeetingRoom PricingOptionType = "meeting_room" VirtualOffice PricingOptionType = "virtual_office" EventSpace PricingOptionType = "event_space" Membership PricingOptionType = "membership" )
Every pricing option type the catalogue uses.
type Property ¶
type Property struct {
Slug string `json:"slug"`
Title string `json:"title"`
CountryCode string `json:"countryCode,omitempty"`
City *PlaceRef `json:"city,omitempty"`
Zone *PlaceRef `json:"zone,omitempty"`
Operator *OperatorRef `json:"operator,omitempty"`
Coordinates *Coordinates `json:"coordinates,omitempty"`
FromPrice *Money `json:"fromPrice,omitempty"`
FromBillingPeriod BillingPeriod `json:"fromBillingPeriod,omitempty"`
Featured bool `json:"featured"`
UpdatedAt string `json:"updatedAt,omitempty"`
Description string `json:"description"`
Address string `json:"address"`
Images []Image `json:"images"`
Amenities []string `json:"amenities"`
Capacity int `json:"capacity,omitempty"`
PricingOptions []PricingOption `json:"pricingOptions"`
MarketPosition *MarketPosition `json:"marketPosition,omitempty"`
// WebURL is the space's page on coworkingview.com, where the current offer
// and an enquiry form live.
WebURL string `json:"webUrl"`
}
Property is the full record for one space.
type PropertySummary ¶
type PropertySummary struct {
Slug string `json:"slug"`
Title string `json:"title"`
CountryCode string `json:"countryCode,omitempty"`
City *PlaceRef `json:"city,omitempty"`
Zone *PlaceRef `json:"zone,omitempty"`
Operator *OperatorRef `json:"operator,omitempty"`
Coordinates *Coordinates `json:"coordinates,omitempty"`
FromPrice *Money `json:"fromPrice,omitempty"`
FromBillingPeriod BillingPeriod `json:"fromBillingPeriod,omitempty"`
Featured bool `json:"featured"`
UpdatedAt string `json:"updatedAt,omitempty"`
Excerpt string `json:"excerpt,omitempty"`
Images []Image `json:"images"`
Amenities []string `json:"amenities"`
Capacity int `json:"capacity,omitempty"`
MarketPosition *MarketPosition `json:"marketPosition,omitempty"`
// DistanceMeters is set when the list was searched near a point.
DistanceMeters *float64 `json:"distanceMeters,omitempty"`
}
PropertySummary is one space in a list.
type RateByType ¶
type RateByType struct {
OptionType PricingOptionType `json:"optionType"`
Median Money `json:"median"`
Min Money `json:"min"`
Max Money `json:"max"`
// SampleSize is how many priced spaces of this type the figure comes from.
SampleSize int `json:"sampleSize"`
}
RateByType is the legacy per-type aggregate. See CityRates.Figures.
type RateFigure ¶
type RateFigure struct {
OptionType PricingOptionType `json:"optionType"`
Median Money `json:"median"`
// P25 and P75 are nil when the sample is too small for quartiles.
P25 *Money `json:"p25"`
P75 *Money `json:"p75"`
SampleSize int `json:"sampleSize"`
PropertyCount int `json:"propertyCount"`
OperatorCount int `json:"operatorCount"`
// InsufficientData is always false on a published figure.
InsufficientData bool `json:"insufficientData"`
}
RateFigure is one workspace type's market figure in a city.
type RateStat ¶
type RateStat struct {
Median Money `json:"median"`
SampleSize int `json:"sampleSize"`
PropertyCount int `json:"propertyCount,omitempty"`
OperatorCount int `json:"operatorCount,omitempty"`
InsufficientData bool `json:"insufficientData,omitempty"`
}
RateStat is a single median with the sample behind it.
type SortBy ¶
type SortBy string
SortBy orders a property list.
const ( // SortRandom is the default: a seeded shuffle, so no space is always first. SortRandom SortBy = "random" SortPriceAsc SortBy = "price-asc" SortNewest SortBy = "date-desc" SortOldest SortBy = "date-asc" SortByDistance SortBy = "distance" // needs Near )
Orderings accepted by Client.ListProperties.
type Suggestion ¶
type Suggestion struct {
// Kind is "country", "city", "zone", "operator" or "property".
Kind string `json:"kind"`
Slug string `json:"slug"`
Label string `json:"label"`
Sublabel string `json:"sublabel,omitempty"`
Center *Coordinates `json:"center,omitempty"`
PropertyCount int `json:"propertyCount,omitempty"`
}
Suggestion is one autocomplete hit.
type UnpublishedCity ¶
type UnpublishedCity struct {
City PlaceRef `json:"city"`
InsufficientData bool `json:"insufficientData"`
}
UnpublishedCity is a city with spaces but too few prices to publish a figure.