Skip to main content

Google Cloud Storage

Prerequisites​

  • A PolyDoc account and API key — sign up for free, no credit card required. The free plan includes 150 PDF conversions per month.
  • A Google Cloud project with a Cloud Storage bucket. Create a bucket
  • A service account with the Storage Object Creator role on the target bucket.

Integration​

Generate a signed URL​

Generate a signed URL with write access. The service account only needs the Storage Object Creator role on the target bucket.
import { Storage } from "@google-cloud/storage";

// npm install @google-cloud/storage
// Set GOOGLE_APPLICATION_CREDENTIALS env var to your service account JSON

const storage = new Storage();

const [presignedUrl] = await storage
.bucket("my-bucket")
.file("invoices/inv-001.pdf")
.getSignedUrl({
version: "v4",
action: "write",
expires: Date.now() + 5 * 60 * 1000, // 5 minutes
contentType: "application/pdf",
});
from google.cloud import storage
from datetime import timedelta

# pip install google-cloud-storage
# Set GOOGLE_APPLICATION_CREDENTIALS env var

client = storage.Client()
bucket = client.bucket("my-bucket")
blob = bucket.blob("invoices/inv-001.pdf")

presigned_url = blob.generate_signed_url(
version="v4",
expiration=timedelta(minutes=5),
method="PUT",
content_type="application/pdf",
)
require "google/cloud/storage"

# gem install google-cloud-storage
# Set GOOGLE_APPLICATION_CREDENTIALS env var

storage = Google::Cloud::Storage.new
bucket = storage.bucket "my-bucket"
file = bucket.file "invoices/inv-001.pdf", skip_lookup: true

presigned_url = file.signed_url(
method: "PUT",
expires: 300,
content_type: "application/pdf",
version: :v4
)
package main

import (
"context"
"fmt"
"time"

"cloud.google.com/go/storage"
"google.golang.org/api/option"
)

// go get cloud.google.com/go/storage

func main() {
ctx := context.Background()
client, _ := storage.NewClient(ctx, option.WithCredentialsFile("service-account.json"))
defer client.Close()

opts := &storage.SignedURLOptions{
Scheme: storage.SigningSchemeV4,
Method: "PUT",
ContentType: "application/pdf",
Expires: time.Now().Add(5 * time.Minute),
}

presignedUrl, _ := client.Bucket("my-bucket").SignedURL("invoices/inv-001.pdf", opts)
fmt.Println(presignedUrl)
}
<?php
require 'vendor/autoload.php';

// composer require google/cloud-storage

use Google\Cloud\Storage\StorageClient;

$storage = new StorageClient();
$bucket = $storage->bucket('my-bucket');
$object = $bucket->object('invoices/inv-001.pdf');

$presignedUrl = $object->signedUrl(
new \DateTime('+5 minutes'),
[
'method' => 'PUT',
'contentType' => 'application/pdf',
'version' => 'v4',
]
);
// The google-cloud-storage crate does not expose a direct presigned-URL API.
// Use the gcloud CLI or a service-account-signed HTTP request instead.
// Below is the CLI equivalent — see the CLI tab for the recommended approach.

// Alternatively, construct a V4 signed URL using the google_cloud_auth crate
// and the GCS signing spec:
// https://cloud.google.com/storage/docs/access-control/signed-urls
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.HttpMethod;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import java.net.URL;
import java.util.concurrent.TimeUnit;

// implementation 'com.google.cloud:google-cloud-storage:2.x.x' in build.gradle

public class Main {
public static void main(String[] args) {
Storage storage = StorageOptions.getDefaultInstance().getService();

BlobInfo blobInfo = BlobInfo.newBuilder("my-bucket", "invoices/inv-001.pdf")
.setContentType("application/pdf")
.build();

URL presignedUrl = storage.signUrl(
blobInfo,
5, TimeUnit.MINUTES,
Storage.SignUrlOption.httpMethod(HttpMethod.PUT),
Storage.SignUrlOption.withV4Signature()
);

System.out.println(presignedUrl);
}
}
using Google.Cloud.Storage.V1;
using System;

// NuGet: Google.Cloud.Storage.V1

var urlSigner = UrlSigner.FromServiceAccountPath("service-account.json");

string presignedUrl = await urlSigner.SignAsync(
bucket: "my-bucket",
objectName: "invoices/inv-001.pdf",
duration: TimeSpan.FromMinutes(5),
requestTemplate: UrlSigner.RequestTemplate
.FromBucket("my-bucket")
.WithHttpMethod(System.Net.Http.HttpMethod.Put)
.WithContentHeaders(new System.Net.Http.Headers.HttpContentHeaders
{
ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/pdf")
})
);

Console.WriteLine(presignedUrl);
# Requires gcloud CLI authenticated with a service account that has
# the Storage Object Creator role on the bucket.
gcloud storage sign-url gs://my-bucket/invoices/inv-001.pdf \
--http-verb=PUT \
--duration=5m \
--headers=Content-Type:application/pdf

PolyDoc API request​

Pass the signed URL to PolyDoc. The converted document is uploaded directly to your GCS bucket. The response is JSON containing the permanent object URL.
const response = await fetch('https://api.polydoc.tech/pdf/convert', {
method: 'POST',
headers: {
'Authorization': 'Bearer <YOUR_API_KEY>',
'Content-Type': 'application/json',
'X-Sandbox': 'true',
},
body: JSON.stringify({
source: '<html><body><h1>Hello</h1></body></html>',
cloudStorage: { presignedUrl },
}),
});

const result = await response.json();
console.log(result.data.url);
// → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
import requests

headers = {
'Authorization': 'Bearer <YOUR_API_KEY>',
'Content-Type': 'application/json',
'X-Sandbox': 'true',
}

data = {
'source': '<html><body><h1>Hello</h1></body></html>',
'cloudStorage': {'presignedUrl': presigned_url},
}

response = requests.post(
'https://api.polydoc.tech/pdf/convert',
headers=headers,
json=data,
)

result = response.json()
print(result['data']['url'])
# → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
require 'net/http'
require 'uri'
require 'json'

uri = URI.parse('https://api.polydoc.tech/pdf/convert')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer <YOUR_API_KEY>'
request['Content-Type'] = 'application/json'
request['X-Sandbox'] = 'true'
request.body = JSON.dump({
'source' => '<html><body><h1>Hello</h1></body></html>',
'cloudStorage' => { 'presignedUrl' => presigned_url },
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end

result = JSON.parse(response.body)
puts result['data']['url']
# → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
package main

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)

func main() {
payload := map[string]interface{}{
"source": "<html><body><h1>Hello</h1></body></html>",
"cloudStorage": map[string]string{
"presignedUrl": presignedUrl,
},
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.polydoc.tech/pdf/convert", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer <YOUR_API_KEY>")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Sandbox", "true")

client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()

var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["data"].(map[string]interface{})["url"])
// → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
}
<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.polydoc.tech/pdf/convert');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'source' => '<html><body><h1>Hello</h1></body></html>',
'cloudStorage' => ['presignedUrl' => $presignedUrl],
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer <YOUR_API_KEY>',
'Content-Type: application/json',
'X-Sandbox: true',
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
echo $result['data']['url'];
// → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
use reqwest::Client;
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();

let res = client
.post("https://api.polydoc.tech/pdf/convert")
.header("Authorization", "Bearer <YOUR_API_KEY>")
.header("X-Sandbox", "true")
.json(&json!({
"source": "<html><body><h1>Hello</h1></body></html>",
"cloudStorage": { "presignedUrl": presigned_url }
}))
.send()
.await?;

let result: serde_json::Value = res.json().await?;
println!("{}", result["data"]["url"]);
// → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf

Ok(())
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;

public class Main {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
ObjectMapper mapper = new ObjectMapper();

String body = mapper.writeValueAsString(Map.of(
"source", "<html><body><h1>Hello</h1></body></html>",
"cloudStorage", Map.of("presignedUrl", presignedUrl)
));

HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://api.polydoc.tech/pdf/convert"))
.header("Authorization", "Bearer <YOUR_API_KEY>")
.header("Content-Type", "application/json")
.header("X-Sandbox", "true")
.POST(BodyPublishers.ofString(body))
.build();

HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);

var result = mapper.readValue(response.body(), Map.class);
System.out.println(((Map) result.get("data")).get("url"));
// → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
}
}
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "<YOUR_API_KEY>");
client.DefaultRequestHeaders.Add("X-Sandbox", "true");

var payload = JsonSerializer.Serialize(new {
source = "<html><body><h1>Hello</h1></body></html>",
cloudStorage = new { presignedUrl }
});

var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.polydoc.tech/pdf/convert", content);
var json = await response.Content.ReadAsStringAsync();

using var doc = JsonDocument.Parse(json);
Console.WriteLine(doc.RootElement.GetProperty("data").GetProperty("url").GetString());
// → https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf
curl --location \
--request POST 'https://api.polydoc.tech/pdf/convert' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--header 'X-Sandbox: true' \
--data-raw '{
"source": "<html><body><h1>Hello</h1></body></html>",
"cloudStorage": {
"presignedUrl": "<PRESIGNED_URL>"
}
}'

# Response:
# {
# "success": true,
# "data": {
# "url": "https://my-bucket.s3.eu-central-1.amazonaws.com/invoices/inv-001.pdf"
# }
# }

Response​

On success, PolyDoc returns the permanent object URL.
{
"success": true,
"message": "PDF uploaded to cloud storage.",
"data": {
"url": "https://storage.googleapis.com/my-bucket/invoices/inv-001.pdf"
}
}

Tips​