Google Vision API OCR in n8n returns empty (response: [ {} ])

Hi Community,

I am new to n8n with non techincal background, and keen to learn. While working on a workflow in n8n where I want to extract text from business cards using Google Vision API.

My setup:

My JSON body looks like this:

{
“requests”: [
{
“image”: {
“content”: “{{$json.data}}”
},
“features”: [
{
“type”: “DOCUMENT_TEXT_DETECTION”
}
],
“imageContext”: {
“languageHints”: [“en”, “hi”]
}
}
]
}

Problem:

  • The request succeeds (status 200), but the response is always:

  • No text annotations are being returned.

What I tried:

  • Checked headers (Content-Type: application/json)
  • Switched between “Specify Body: Using JSON” vs “Body Parameters”
  • Confirmed Base64 is passed correctly from Extract node
  • Tried both "TEXT_DETECTION" and "DOCUMENT_TEXT_DETECTION"

But still response is empty.

Can anyone suggest what might be wrong in my workflow? Am I building the JSON body correctly in n8n, or is there something missing in the Google Vision request?

Thanks!

Hey @Kartik_Janakiraman_N hope all is good. Welcome to the community.

Here is an example for you, which uses google vision api on a locally sourced pdf file.

Notice how I extract the base64 from the binary first and then use it in the JSON body.

1 Like

Thank you jobbson… appreciate this…

below is the workflow that i had made… do u think this looks goood or have i erred in my workflow ?

Looking forward to hearing from u.

Kjn

Hey @Kartik_Janakiraman_N thanks for getting back about this.

I encourage you to share your workflow instead of the screenshot, as this will allow us to better analyze the flow to see if any problems can be found. Overall this looks reasonable. The trigger, image file gets downloaded and edited, then converted to base64, analyzed with Google Vision, then Code cleans up the text I guess and that get’s written to a Google Sheet. If this is the case, then yeah, makes sense to me.

If this helped you, kindly mark the answer as Solution. Thank you.

Cheers.

Hi… here you go…

{
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "value": "16v8LZMUeKt5BAyyrafllDYt7aLnhNhgVDj1xEN3H9RzG2JCBXIrM7HimzwGUL4DPLOAaF1bB",
          "mode": "list",
          "cachedResultName": "Upload the image of the Business Card (File responses)",
          "cachedResultUrl": "https://drive.google.com/drive/folders/16v8LZMUeKt5BAyyrafllDYt7aLnhNhgVDj1xEN3H9RzG2JCBXIrM7HimzwGUL4DPLOAaF1bB"
        },
        "event": "fileCreated",
        "options": {}
      },
      "type": "n8n-nodes-base.googleDriveTrigger",
      "typeVersion": 1,
      "position": [
        -144,
        -240
      ],
      "id": "a5290cbd-e385-4835-b330-b5ab680cfdc1",
      "name": "Google Drive Trigger",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "RhPzQQic9eSpzPau",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "={{$json.id || $json.fileId}}\n  ",
          "mode": "id"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        48,
        -240
      ],
      "id": "907f9651-cf82-4a16-95e7-9cf665f6e7ba",
      "name": "Download file",
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "RhPzQQic9eSpzPau",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "operation": "binaryToPropery",
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        384,
        -240
      ],
      "id": "3110c60c-7d5b-4cf2-97f6-2f3fe33088a1",
      "name": "Extract from File"
    },
    {
      "parameters": {
        "operation": "resize",
        "width": 1600,
        "height": null,
        "options": {
          "format": "jpeg",
          "quality": 80
        }
      },
      "type": "n8n-nodes-base.editImage",
      "typeVersion": 1,
      "position": [
        224,
        -240
      ],
      "id": "c8b73e09-e37e-417d-907e-99487cb38862",
      "name": "Edit Image"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://vision.googleapis.com/v1/images:annotate?key=AIzaSyAYuorNgXn2msbtH0NQBMzdM1PlK5JRQpU",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "requests",
              "value": "={{\n  [\n    {\n      image: { content: $json.data },\n      features: [{ type: 'DOCUMENT_TEXT_DETECTION' }],\n      imageContext: { languageHints: ['en','hi'] }\n    }\n  ]\n}}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        560,
        -240
      ],
      "id": "cd3e57a5-0def-433b-aeea-3bad20707aab",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Vision API response se clean fields\nconst res   = $json.responses?.[0] ?? {};\nconst text  = res.fullTextAnnotation?.text ?? '';\nconst lines = text.split(/\\r?\\n/).map(l => l.trim()).filter(Boolean);\nconst joined = lines.join(' ');\n\n// ---------- helpers ----------\nconst uniq = arr => [...new Set((arr || []).filter(Boolean).map(s => s.trim()))];\nconst escapeRegExp = s => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\nconst hasSome = (hay, words) => {\n  const h = ` ${hay.toLowerCase()} `;\n  return (words || []).some(w => {\n    const patt = new RegExp(`\\\\b${escapeRegExp(String(w).toLowerCase())}\\\\b`, 'i');\n    return patt.test(h);\n  });\n};\n\n// ---------- email / phone / urls ----------\nconst emails = joined.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi) || [];\n\nconst phonesRaw =\n  joined.match(/(?:\\+\\d{1,3}[\\s-]?)?(?:\\(?\\d{2,4}\\)?[\\s-]?)?\\d{3,5}[\\s-]?\\d{3,5}/g) || [];\nconst phones = uniq(phonesRaw.map(p => p.replace(/[^\\d+]/g, '')));\n\nconst urls =\n  (joined.match(/\\bhttps?:\\/\\/[^\\s)]+/gi) || []).map(u => u.replace(/[),.]+$/, ''));\n\n// ---------- guess some basics ----------\nconst name    = lines[0] || $json.name || '';\nconst company = $json.company || '';\nconst jobTitle = lines.find(l => /(manager|director|executive|consultant|engineer|head|chief|officer|founder|president)/i.test(l)) || '';\nconst website = urls.find(u => !/linkedin\\.com/i.test(u)) || '';\nconst linkedin = urls.find(u => /linkedin\\.com/i.test(u)) || '';\nconst address = lines.find(l => /(street|st\\.|road|rd\\.|avenue|ave\\.|city|state|zip|po box)/i.test(l)) || '';\n\n// ---------- supplier tags ----------\nlet tags = [];\nconst buckets = [\n  ['airline',   'airline', 'airways', 'airlines', 'aviation', 'airport'],\n  ['hotel',     'hotel', 'resort', 'spa', 'lodging', 'lodge', 'hostel', 'villa'],\n  ['dmc',       'dmc', 'destination management', 'ground handler'],\n  ['transport', 'transport', 'transfer', 'taxi', 'car rental', 'chauffeur', 'limo', 'bus', 'coach', 'rail', 'ferry'],\n  ['cruise',    'cruise', 'cruiseship', 'cruiseline'],\n  ['events',    'mice', 'event', 'events', 'conference', 'exhibition', 'congress', 'venue', 'banquet', 'catering'],\n  ['insurance', 'insurance', 'insurer', 'travel insurance'],\n  ['maas',      'meet and assist', 'maas', 'lounge', 'meet & assist'],\n  ['ota',       'ota', 'wholesaler', 'consolidator', 'tour operator', 'touroperator', 'gsa'],\n  ['technology','technology', 'booking engine', 'pms', 'channel manager', 'software', 'platform', 'api', 'saas']\n];\n\nbuckets.forEach(([label, ...words]) => {\n  if (hasSome(joined, words)) tags.push(`#${label}`);\n});\n\n// domain heuristics\nif (urls.some(u => /linkedin\\.com/i.test(u))) tags.push('#linkedin');\nif (urls.some(u => /(marriott|hilton|hyatt|accor|ihg|radisson|taj|oberoi)/i.test(u))) tags.push('#hotel');\n\ntags = uniq(tags);\n\n// ---------- output ----------\nreturn {\n  json: {\n    ...$json,\n    name,\n    company,\n    jobTitle,\n    website,\n    linkedin,\n    address,\n    email: emails[0] || $json.email || '',\n    phones,\n    urls,\n    ocr_text: joined,\n    supplier: tags.join(', ')\n  }\n};\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -112,
        0
      ],
      "id": "c1b310e1-2eb8-43a5-8ee0-8648d48c51ea",
      "name": "Code"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1aae3-uH2AR8YfbK8VQ-lGIA27qx66V-VbI4q7cann8c",
          "mode": "list",
          "cachedResultName": "BHT-CCD",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1aae3-uH2AR8YfbK8VQ-lGIA27qx66V-VbI4q7cann8c/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "CCD",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "FullName": "={{ $json.name || ''}}",
            "ContactID": "=",
            "JobTitle": "={{ $json.jobTitle || ''}}",
            "Company": "={{ $json.company || ''}}",
            "Email": "={{ $json.email || ''}}",
            "PhoneMobile": "={{ $json.phones || ''}}",
            "Website": "={{ $json.website || ''}}",
            "AddressLine1": "={{ $json.address || ''}}",
            "AltEmail": "=",
            "PhoneWork": "=",
            "AltPhone": "=",
            "WhatsApp": "=",
            "LinkedIn": "=",
            "City": "=",
            "Country": "=",
            "ImageURL": "=",
            "Supplier": "="
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "ContactID",
              "displayName": "ContactID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "FullName",
              "displayName": "FullName",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "JobTitle",
              "displayName": "JobTitle",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Company",
              "displayName": "Company",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Email",
              "displayName": "Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Supplier",
              "displayName": "Supplier",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "AltEmail",
              "displayName": "AltEmail",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "PhoneMobile",
              "displayName": "PhoneMobile",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "PhoneWork",
              "displayName": "PhoneWork",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "AltPhone",
              "displayName": "AltPhone",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "WhatsApp",
              "displayName": "WhatsApp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Website",
              "displayName": "Website",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "LinkedIn",
              "displayName": "LinkedIn",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "AddressLine1",
              "displayName": "AddressLine1",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "City",
              "displayName": "City",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Country",
              "displayName": "Country",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "ImageURL",
              "displayName": "ImageURL",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "UpdatedAt",
              "displayName": "UpdatedAt",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        96,
        0
      ],
      "id": "7d16d2a5-859d-404f-9a0a-23ecb348ada8",
      "name": "Append row in sheet",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "NKd0XUqNaUfRRoJV",
          "name": "Google Sheets account"
        }
      }
    }
  ],
  "connections": {
    "Google Drive Trigger": {
      "main": [
        [
          {
            "node": "Download file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download file": {
      "main": [
        [
          {
            "node": "Edit Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Image": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "c79580bb59ac3f226b63dd4411276a5d34f294424b94f8ca4ae9ddc04d470c23"
  }
}

Please share ur feedback.

Yeah, this looks reasonable to me overall, obviously you need to decide if this solves your task at hand, but just by observing the flow, if should be pretty close.

Please mark the answer as Solution if it helped you. Thank you.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.