Search code examples
rgoogle-apihttrgoogle-url-shortener

Client error: (400) Bad Request when URL provided from a variable


I am trying to shorten several thousands of URLs using Google API. I am using httr to do POST. Whenever I provide a URL to post as a variable I get "Client error: (400) Bad Request" but when the same URL is provides directly as string (for example "http://www.google.com") everything works fine. A minimal example is provided below:

library(httr)
library(httpuv)

# enter data
mydata <- data.frame(Link = "http://www.google.com")

# 1. Find OAuth settings for google:
#    https://developers.google.com/accounts/docs/OAuth2InstalledApp
oauth_endpoints("google")

# 2. Register an application at https://cloud.google.com/console#/project
myapp <- oauth_app("google", key = "key goes here", secret = "secret goes here")

# 3. Get OAuth credentials
google_token <- oauth2.0_token(oauth_endpoints("google"), myapp, scope = "https://www.googleapis.com/auth/urlshortener")

This returns Error: client error: (400) Bad Request

req <- POST('https://www.googleapis.com/urlshortener/v1/url?fields=id',
       add_headers("Content-Type"="application/json"),
       body='{"longUrl": mydata$Link[1]}', config(token = google_token))
stop_for_status(req)

This works just fine

req <- POST('https://www.googleapis.com/urlshortener/v1/url?fields=id',
       add_headers("Content-Type"="application/json"),
       body='{"longUrl": "http://www.google.com"}', config(token = google_token))
stop_for_status(req)

I tried encoding URLs, testing both http and https and but none of the above seem to have any effect. Could anyone provide me with any suggestions? Thank you in advance!

-jacek


Solution

  • You have a couple of problems.

    First: data frames coerce character vectors to factors:

    mydata <- data.frame(link = "http://www.google.com")
    class(mydata$link)
    #> [1] "factor"
    
    mydata <- data.frame(link = "http://www.google.com", stringsAsFactors = FALSE)
    class(mydata$link)
    #> [1] "character"
    

    Second, you're sending '{"longUrl": mydata$Link[1]}' to google - i.e. the longUrl you are submitting is mydata$Link[1], not http://www.google.com. It's easiest to fix that by using jsonlite to do the encoding:

    req <- POST('https://www.googleapis.com/urlshortener/v1/url?fields=id',
      body = list(longUrl = jsonlite::unbox(mydata$link[1]),
      encode = "json",
      config(token = google_token)
    )
    stop_for_status(req)
    

    (Unfortunately the unbox() is necessary because jsonlite defaults to converting length-1 vetors into js arrays, not scalars.)