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"
}
}