curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"tags": [
"<string>"
],
"selectedPostIds": [
"<string>"
],
"query": "<string>",
"filters": "<string>"
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags"
payload = {
"tags": ["<string>"],
"selectedPostIds": ["<string>"],
"query": "<string>",
"filters": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
tags: ['<string>'],
selectedPostIds: ['<string>'],
query: '<string>',
filters: '<string>'
})
};
fetch('https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'tags' => [
'<string>'
],
'selectedPostIds' => [
'<string>'
],
'query' => '<string>',
'filters' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags"
payload := strings.NewReader("{\n \"tags\": [\n \"<string>\"\n ],\n \"selectedPostIds\": [\n \"<string>\"\n ],\n \"query\": \"<string>\",\n \"filters\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"tags\": [\n \"<string>\"\n ],\n \"selectedPostIds\": [\n \"<string>\"\n ],\n \"query\": \"<string>\",\n \"filters\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"tags\": [\n \"<string>\"\n ],\n \"selectedPostIds\": [\n \"<string>\"\n ],\n \"query\": \"<string>\",\n \"filters\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true,
"requestedCount": 123,
"updatedCount": 123,
"capped": true,
"tags": [
"<string>"
],
"results": [
{
"postId": "<string>",
"companyId": "<string>",
"analyticsTags": [
"<string>"
]
}
],
"errors": [
{
"postId": "<string>",
"companyId": "<string>",
"error": "<string>"
}
]
}
}{
"error": {
"code": "unauthorized",
"message": "Missing bearer access token",
"requestId": "req_..."
}
}{
"error": {
"code": "insufficient_scope",
"message": "Requires scope 'campaigns:write'",
"requestId": "req_..."
}
}{
"error": {
"code": "not_found",
"message": "Resource not found",
"requestId": "req_..."
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded",
"requestId": "req_..."
}
}Bulk update analytics tags
bulk add / remove / replace analytics tags across the token tenant’s posts. Requires the posts:write scope.
curl --request POST \
--url https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"tags": [
"<string>"
],
"selectedPostIds": [
"<string>"
],
"query": "<string>",
"filters": "<string>"
}
'import requests
url = "https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags"
payload = {
"tags": ["<string>"],
"selectedPostIds": ["<string>"],
"query": "<string>",
"filters": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
tags: ['<string>'],
selectedPostIds: ['<string>'],
query: '<string>',
filters: '<string>'
})
};
fetch('https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'tags' => [
'<string>'
],
'selectedPostIds' => [
'<string>'
],
'query' => '<string>',
'filters' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags"
payload := strings.NewReader("{\n \"tags\": [\n \"<string>\"\n ],\n \"selectedPostIds\": [\n \"<string>\"\n ],\n \"query\": \"<string>\",\n \"filters\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"tags\": [\n \"<string>\"\n ],\n \"selectedPostIds\": [\n \"<string>\"\n ],\n \"query\": \"<string>\",\n \"filters\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sideshift.app/api/oauth/v1/posts/bulk-analytics-tags")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"tags\": [\n \"<string>\"\n ],\n \"selectedPostIds\": [\n \"<string>\"\n ],\n \"query\": \"<string>\",\n \"filters\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"success": true,
"requestedCount": 123,
"updatedCount": 123,
"capped": true,
"tags": [
"<string>"
],
"results": [
{
"postId": "<string>",
"companyId": "<string>",
"analyticsTags": [
"<string>"
]
}
],
"errors": [
{
"postId": "<string>",
"companyId": "<string>",
"error": "<string>"
}
]
}
}{
"error": {
"code": "unauthorized",
"message": "Missing bearer access token",
"requestId": "req_..."
}
}{
"error": {
"code": "insufficient_scope",
"message": "Requires scope 'campaigns:write'",
"requestId": "req_..."
}
}{
"error": {
"code": "not_found",
"message": "Resource not found",
"requestId": "req_..."
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded",
"requestId": "req_..."
}
}Authorizations
OAuth 2.1 authorization code + PKCE (S256). Tokens are tenant-bound (company_id) and scoped. Discover endpoints via /.well-known/oauth-authorization-server.
Body
At least one of selectedPostIds, query, or filters is required, otherwise the route returns 400 (refusing to tag the entire company by default).
How to apply the tags across all matched posts.
add, remove, replace Tag strings to apply (at least one after sanitization).
1Explicit post ids to target.
Free-text search over post title/creator/contractorName to select targets. Pass "*" to mean all posts.
Additional Typesense filter_by expression to narrow targets.
Response
Per-post results and errors for the bulk tag operation.
Show child attributes
Show child attributes