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